From c238c479b8bc65c7a5ed42bda63630dca5376d09 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 19 Dec 2023 10:39:04 +0100 Subject: [PATCH 01/25] Changelog placeholder --- .../dependency-extraction-webpack-plugin/CHANGELOG.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index 78de3e4142c87..a3343654ca0e0 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -4,8 +4,12 @@ ### Breaking Changes -- Drop support for webpack 4. -- Drop support for Node.js versions < 18. +- Drop support for webpack 4. +- Drop support for Node.js versions < 18. + +### New Features + +- The plugin now supports generating ECMAScript modules output **@TODO @sirreal complete this**. ## 4.31.0 (2023-12-13) @@ -145,6 +149,6 @@ ## 1.0.0 (2019-05-21) -### New Feature +### New Features - Introduce the `@wordpress/dependency-extraction-webpack-plugin` package. From 171c235db33443cc3ddda544db0d874a6bcb8727 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 21 Dec 2023 13:57:37 +0100 Subject: [PATCH 02/25] Implement Module API output --- .../lib/.eslintrc.json | 2 - .../lib/index.js | 107 ++++++++++-- .../lib/types.d.ts | 1 + .../lib/util.js | 16 ++ .../test/build.js | 163 ++++++++++-------- .../fixtures/combine-assets/webpack.config.js | 5 + .../fixtures/dynamic-import/webpack.config.js | 10 +- .../webpack.config.js | 10 +- .../has-extension-suffix/webpack.config.js | 10 +- .../webpack.config.js | 5 + .../option-output-filename/webpack.config.js | 5 + .../test/fixtures/overrides/webpack.config.js | 12 ++ .../runtime-chunk-single/webpack.config.js | 10 +- .../fixtures/style-imports/webpack.config.js | 8 +- .../fixtures/wordpress-interactivity/index.js | 13 ++ .../wordpress-interactivity/webpack.config.js | 17 ++ .../wordpress-require/webpack.config.js | 10 +- .../test/fixtures/wordpress/webpack.config.js | 10 +- 18 files changed, 317 insertions(+), 97 deletions(-) create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/index.js create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/webpack.config.js diff --git a/packages/dependency-extraction-webpack-plugin/lib/.eslintrc.json b/packages/dependency-extraction-webpack-plugin/lib/.eslintrc.json index 305ba4347aedd..5e1457134b16a 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/.eslintrc.json +++ b/packages/dependency-extraction-webpack-plugin/lib/.eslintrc.json @@ -1,6 +1,4 @@ { - // Use the default eslint parser. Prevent babel transforms. - "parser": "espree", "env": { "node": true } diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index 98b484672ef3f..333b9c27bbd89 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -11,10 +11,13 @@ const { createHash } = webpack.util; */ const { defaultRequestToExternal, + defaultRequestToExternalModule, defaultRequestToHandle, } = require( './util' ); const { RawSource } = webpack.sources; +const { AsyncDependenciesBlock } = webpack; + const defaultExternalizedReportFileName = 'externalized-dependencies.json'; class DependencyExtractionWebpackPlugin { @@ -32,27 +35,48 @@ class DependencyExtractionWebpackPlugin { options ); - /* + /** * Track requests that are externalized. * * Because we don't have a closed set of dependencies, we need to track what has * been externalized so we can recognize them in a later phase when the dependency * lists are generated. + * + * @type {Set} */ this.externalizedDeps = new Set(); - // Offload externalization work to the ExternalsPlugin. + /** + * Should we use modules. This will be set later to match webpack's + * output.module option. + * + * @type {boolean} + */ + this.useModules = false; + + /** + * Offload externalization work to the ExternalsPlugin. + */ this.externalsPlugin = new webpack.ExternalsPlugin( 'window', this.externalizeWpDeps.bind( this ) ); } + /** + * @param {webpack.ExternalItemFunctionData} data + * @param { ( err?: null | Error, result?: string | string[] ) => void } callback + */ externalizeWpDeps( { request }, callback ) { let externalRequest; - // Handle via options.requestToExternal first. - if ( typeof this.options.requestToExternal === 'function' ) { + // Handle via options.requestToExternal(Module) first. + if ( this.useModules ) { + if ( typeof this.options.requestToExternalModule === 'function' ) { + externalRequest = + this.options.requestToExternalModule( request ); + } + } else if ( typeof this.options.requestToExternal === 'function' ) { externalRequest = this.options.requestToExternal( request ); } @@ -61,7 +85,9 @@ class DependencyExtractionWebpackPlugin { typeof externalRequest === 'undefined' && this.options.useDefaults ) { - externalRequest = defaultRequestToExternal( request ); + externalRequest = this.useModules + ? defaultRequestToExternalModule( request ) + : defaultRequestToExternal( request ); } if ( externalRequest ) { @@ -73,6 +99,10 @@ class DependencyExtractionWebpackPlugin { return callback(); } + /** + * @param {string} request + * @return {string} Mapped dependency name + */ mapRequestToDependency( request ) { // Handle via options.requestToHandle first. if ( typeof this.options.requestToHandle === 'function' ) { @@ -94,6 +124,10 @@ class DependencyExtractionWebpackPlugin { return request; } + /** + * @param {any} asset Asset Data + * @return {string} Stringified asset data suitable for output + */ stringify( asset ) { if ( this.options.outputFormat === 'php' ) { return ` /\.js$/i.test( f ) ); + const jsExtensionRegExp = this.useModules ? /\.m?js$/i : /\.js$/i; + + const chunkJSFile = chunkFiles.find( ( f ) => + jsExtensionRegExp.test( f ) + ); if ( ! chunkJSFile ) { // There's no JS file in this chunk, no work for us. Typically a `style.css` from cache group. continue; } - const chunkDeps = new Set(); + /** @type {Set} */ + const chunkStaticDeps = new Set(); + /** @type {Set} */ + const chunkDynamicDeps = new Set(); + if ( injectPolyfill ) { - chunkDeps.add( 'wp-polyfill' ); + chunkStaticDeps.add( 'wp-polyfill' ); } - const processModule = ( { userRequest } ) => { + const processModule = ( m ) => { + const { userRequest } = m; if ( this.externalizedDeps.has( userRequest ) ) { - chunkDeps.add( this.mapRequestToDependency( userRequest ) ); + if ( this.useModules ) { + const isDynamic = Array.prototype.every.call( + compilation.moduleGraph.getIncomingConnections( m ), + ( { dependency } ) => { + return ( + compilation.moduleGraph.getParentBlock( + dependency + ) instanceof AsyncDependenciesBlock + ); + } + ); + + ( isDynamic ? chunkDynamicDeps : chunkStaticDeps ).add( + userRequest + ); + } else { + chunkStaticDeps.add( + this.mapRequestToDependency( userRequest ) + ); + } } }; // Search for externalized modules in all chunks. - const modulesIterable = - compilation.chunkGraph.getChunkModules( chunk ); - for ( const chunkModule of modulesIterable ) { + for ( const chunkModule of compilation.chunkGraph.getChunkModulesIterable( + chunk + ) ) { processModule( chunkModule ); // Loop through submodules of ConcatenatedModule. if ( chunkModule.modules ) { @@ -209,8 +279,13 @@ class DependencyExtractionWebpackPlugin { .slice( 0, hashDigestLength ); const assetData = { - // Get a sorted array so we can produce a stable, stringified representation. - dependencies: Array.from( chunkDeps ).sort(), + dependencies: [ + // Sort these so we can produce a stable, stringified representation. + ...Array.from( chunkStaticDeps ).sort(), + ...Array.from( chunkDynamicDeps ) + .sort() + .map( ( id ) => ( { id, type: 'dynamic' } ) ), + ], version: contentHash, }; @@ -231,7 +306,7 @@ class DependencyExtractionWebpackPlugin { '.asset.' + ( outputFormat === 'php' ? 'php' : 'json' ); assetFilename = compilation .getPath( '[file]', { filename: chunkJSFile } ) - .replace( /\.js$/i, suffix ); + .replace( /\.m?js$/i, suffix ); } // Add source and file into compilation for webpack to output. diff --git a/packages/dependency-extraction-webpack-plugin/lib/types.d.ts b/packages/dependency-extraction-webpack-plugin/lib/types.d.ts index 179b4dab593bd..c4a4af52b1b2f 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/types.d.ts +++ b/packages/dependency-extraction-webpack-plugin/lib/types.d.ts @@ -13,6 +13,7 @@ declare interface DependencyExtractionWebpackPluginOptions { outputFormat?: 'php' | 'json'; outputFilename?: string | Function; requestToExternal?: ( request: string ) => string | string[] | undefined; + requestToExternalModule?: ( request: string ) => string | undefined; requestToHandle?: ( request: string ) => string | undefined; combinedOutputFile?: string | null; combineAssets?: boolean; diff --git a/packages/dependency-extraction-webpack-plugin/lib/util.js b/packages/dependency-extraction-webpack-plugin/lib/util.js index bd328430313ce..88ab4695d286c 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/util.js +++ b/packages/dependency-extraction-webpack-plugin/lib/util.js @@ -56,6 +56,21 @@ function defaultRequestToExternal( request ) { } } +/** + * Default request to external module transformation + * + * Currently only @wordpress/interactivity + * + * @param {string} request Module request (the module name in `import from`) to be transformed + * @return {string|undefined} The resulting external definition. Return `undefined` + * to ignore the request. Return `string` to map the request to an external. This may simply be returning the request, e.g. `@wordpress/interactivity` maps to the external `@wordpress/interactivity`. + */ +function defaultRequestToExternalModule( request ) { + if ( request === '@wordpress/interactivity' ) { + return request; + } +} + /** * Default request to WordPress script handle transformation * @@ -101,5 +116,6 @@ function camelCaseDash( string ) { module.exports = { camelCaseDash, defaultRequestToExternal, + defaultRequestToExternalModule, defaultRequestToHandle, }; diff --git a/packages/dependency-extraction-webpack-plugin/test/build.js b/packages/dependency-extraction-webpack-plugin/test/build.js index 7c84e4b0bcc8b..8a9c607eb5378 100644 --- a/packages/dependency-extraction-webpack-plugin/test/build.js +++ b/packages/dependency-extraction-webpack-plugin/test/build.js @@ -13,89 +13,108 @@ const configFixtures = fs.readdirSync( fixturesPath ).sort(); afterAll( () => rimraf( path.join( __dirname, 'build' ) ) ); -describe( 'DependencyExtractionWebpackPlugin scripts', () => { - describe.each( configFixtures )( 'Webpack `%s`', ( configCase ) => { - const testDirectory = path.join( fixturesPath, configCase ); - const outputDirectory = path.join( __dirname, 'build', configCase ); +describe.each( /** @type {const} */ ( [ 'scripts', 'modules' ] ) )( + 'DependencyExtractionWebpackPlugin %s', + ( moduleMode ) => { + describe.each( configFixtures )( 'Webpack `%s`', ( configCase ) => { + const testDirectory = path.join( fixturesPath, configCase ); + const outputDirectory = path.join( + __dirname, + 'build', + moduleMode, + configCase + ); - beforeEach( () => { - rimraf( outputDirectory ); - mkdirp( outputDirectory ); - } ); + beforeEach( () => { + rimraf( outputDirectory ); + mkdirp( outputDirectory ); + } ); - // This afterEach is necessary to prevent watched tests from retriggering on every run. - afterEach( () => rimraf( outputDirectory ) ); + // This afterEach is necessary to prevent watched tests from retriggering on every run. + afterEach( () => rimraf( outputDirectory ) ); - test( 'should produce expected output', async () => { - const options = Object.assign( - { - target: 'web', - context: testDirectory, - entry: './index.js', - mode: 'production', - optimization: { - minimize: false, - chunkIds: 'named', - moduleIds: 'named', + test( 'should produce expected output', async () => { + const options = Object.assign( + { + target: 'web', + context: testDirectory, + entry: './index.js', + mode: 'production', + optimization: { + minimize: false, + chunkIds: 'named', + moduleIds: 'named', + }, + output: {}, + experiments: {}, }, - output: {}, - experiments: {}, - }, - require( path.join( testDirectory, 'webpack.config.js' ) ) - ); - options.output.path = outputDirectory; + require( path.join( testDirectory, 'webpack.config.js' ) ) + ); + options.output.path = outputDirectory; - /** @type {webpack.Stats} */ - const stats = await new Promise( ( resolve, reject ) => - webpack( options, ( err, _stats ) => { - if ( err ) { - return reject( err ); - } - resolve( _stats ); - } ) - ); + if ( moduleMode === 'modules' ) { + options.target = 'es2024'; + options.output.module = true; + options.output.chunkFormat = 'module'; + options.output.library = options.output.library || {}; + options.output.library.type = 'module'; + options.experiments.outputModule = true; + } - if ( stats.hasErrors() ) { - throw new Error( - stats.toString( { errors: true, all: false } ) + /** @type {webpack.Stats} */ + const stats = await new Promise( ( resolve, reject ) => + webpack( options, ( err, _stats ) => { + if ( err ) { + return reject( err ); + } + resolve( _stats ); + } ) ); - } - const assetFiles = glob( - `${ outputDirectory }/+(*.asset|assets).@(json|php)` - ); + if ( stats.hasErrors() ) { + throw new Error( + stats.toString( { errors: true, all: false } ) + ); + } - expect( assetFiles.length ).toBeGreaterThan( 0 ); + const assetFiles = glob( + `${ outputDirectory }/+(*.asset|assets).@(json|php)` + ); - // Asset files should match. - assetFiles.forEach( ( assetFile ) => { - const assetBasename = path.basename( assetFile ); + expect( assetFiles.length ).toBeGreaterThan( 0 ); - expect( fs.readFileSync( assetFile, 'utf-8' ) ).toMatchSnapshot( - `Asset file '${ assetBasename }' should match snapshot` - ); - } ); + // Asset files should match. + assetFiles.forEach( ( assetFile ) => { + const assetBasename = path.basename( assetFile ); - const compareByModuleIdentifier = ( m1, m2 ) => { - const i1 = m1.identifier(); - const i2 = m2.identifier(); - if ( i1 < i2 ) return -1; - if ( i1 > i2 ) return 1; - return 0; - }; + expect( + fs.readFileSync( assetFile, 'utf-8' ) + ).toMatchSnapshot( + `Asset file '${ assetBasename }' should match snapshot` + ); + } ); - // Webpack stats external modules should match. - const externalModules = Array.from( stats.compilation.modules ) - .filter( ( { externalType } ) => externalType ) - .sort( compareByModuleIdentifier ) - .map( ( module ) => ( { - externalType: module.externalType, - request: module.request, - userRequest: module.userRequest, - } ) ); - expect( externalModules ).toMatchSnapshot( - 'External modules should match snapshot' - ); + const compareByModuleIdentifier = ( m1, m2 ) => { + const i1 = m1.identifier(); + const i2 = m2.identifier(); + if ( i1 < i2 ) return -1; + if ( i1 > i2 ) return 1; + return 0; + }; + + // Webpack stats external modules should match. + const externalModules = Array.from( stats.compilation.modules ) + .filter( ( { externalType } ) => externalType ) + .sort( compareByModuleIdentifier ) + .map( ( module ) => ( { + externalType: module.externalType, + request: module.request, + userRequest: module.userRequest, + } ) ); + expect( externalModules ).toMatchSnapshot( + 'External modules should match snapshot' + ); + } ); } ); - } ); -} ); + } +); 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 index 2ce7ba1be98e2..fb7ba94ca8099 100644 --- 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 @@ -11,6 +11,11 @@ module.exports = { plugins: [ new DependencyExtractionWebpackPlugin( { combineAssets: true, + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, } ), ], }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/dynamic-import/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/dynamic-import/webpack.config.js index bfffff3ae7831..6856d328ab7c6 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/dynamic-import/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/dynamic-import/webpack.config.js @@ -4,5 +4,13 @@ const DependencyExtractionWebpackPlugin = require( '../../..' ); module.exports = { - plugins: [ new DependencyExtractionWebpackPlugin() ], + plugins: [ + new DependencyExtractionWebpackPlugin( { + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, + } ), + ], }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/webpack.config.js index 2d5b2e43b735e..f637a4087e3ca 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/function-output-filename/webpack.config.js @@ -9,5 +9,13 @@ module.exports = { return `chunk--${ chunkData.chunk.name }--[name].js`; }, }, - plugins: [ new DependencyExtractionWebpackPlugin() ], + plugins: [ + new DependencyExtractionWebpackPlugin( { + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, + } ), + ], }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/has-extension-suffix/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/has-extension-suffix/webpack.config.js index d814beacdf4dc..ada40c8bf8e54 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/has-extension-suffix/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/has-extension-suffix/webpack.config.js @@ -7,5 +7,13 @@ module.exports = { output: { filename: 'index.min.js', }, - plugins: [ new DependencyExtractionWebpackPlugin() ], + plugins: [ + new DependencyExtractionWebpackPlugin( { + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, + } ), + ], }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/webpack.config.js index e328c817851ce..5056f312c3999 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-function-output-filename/webpack.config.js @@ -9,6 +9,11 @@ module.exports = { outputFilename( chunkData ) { return `chunk--${ chunkData.chunk.name }--[name].asset.php`; }, + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, } ), ], }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/webpack.config.js index 9ec78f6437a18..be52e66165386 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/option-output-filename/webpack.config.js @@ -7,6 +7,11 @@ module.exports = { plugins: [ new DependencyExtractionWebpackPlugin( { outputFilename: '[name]-foo.asset.php', + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, } ), ], }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/overrides/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/overrides/webpack.config.js index 9885e5cade7e9..89eaf6ee4b2f5 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/overrides/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/overrides/webpack.config.js @@ -15,6 +15,18 @@ module.exports = { return [ 'rxjs', 'operators' ]; } }, + requestToExternalModule( request ) { + if ( request === 'rxjs' ) { + return request; + } + + if ( request === 'rxjs/operators' ) { + return request; + } + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, requestToHandle( request ) { if ( request === 'rxjs' || request === 'rxjs/operators' ) { return 'wp-script-handle-for-rxjs'; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/webpack.config.js index e16f6b6b0fe70..1e0824563c52f 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/runtime-chunk-single/webpack.config.js @@ -8,7 +8,15 @@ module.exports = { a: './a', b: './b', }, - plugins: [ new DependencyExtractionWebpackPlugin() ], + plugins: [ + new DependencyExtractionWebpackPlugin( { + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, + } ), + ], optimization: { runtimeChunk: 'single', }, diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/webpack.config.js index 52cb718a579de..332e182e34b04 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/style-imports/webpack.config.js @@ -10,7 +10,13 @@ const DependencyExtractionWebpackPlugin = require( '../../..' ); module.exports = { plugins: [ - new DependencyExtractionWebpackPlugin(), + new DependencyExtractionWebpackPlugin( { + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, + } ), new MiniCSSExtractPlugin(), ], module: { diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/index.js new file mode 100644 index 0000000000000..aea7641d30ed2 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/index.js @@ -0,0 +1,13 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +import _ from 'lodash'; + +// This module should be externalized +// import { store } from '@wordpress/interactivity'; +const { store, getContext } = await import( '@wordpress/interactivity' ); + +store( _.identity( 'my-namespace' ), { state: 'is great' } ); + +getContext(); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/webpack.config.js new file mode 100644 index 0000000000000..04401e3aef989 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/webpack.config.js @@ -0,0 +1,17 @@ +/** + * Internal dependencies + */ +const DependencyExtractionWebpackPlugin = require( '../../..' ); + +module.exports = { + plugins: [ + new DependencyExtractionWebpackPlugin( { + outputFormat: 'json', + requestToExternalModule( request ) { + if ( request.startsWith( 'test-external' ) ) { + return request; + } + }, + } ), + ], +}; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-require/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-require/webpack.config.js index bfffff3ae7831..6856d328ab7c6 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-require/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-require/webpack.config.js @@ -4,5 +4,13 @@ const DependencyExtractionWebpackPlugin = require( '../../..' ); module.exports = { - plugins: [ new DependencyExtractionWebpackPlugin() ], + plugins: [ + new DependencyExtractionWebpackPlugin( { + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, + } ), + ], }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/webpack.config.js index bfffff3ae7831..6856d328ab7c6 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress/webpack.config.js @@ -4,5 +4,13 @@ const DependencyExtractionWebpackPlugin = require( '../../..' ); module.exports = { - plugins: [ new DependencyExtractionWebpackPlugin() ], + plugins: [ + new DependencyExtractionWebpackPlugin( { + requestToExternalModule( request ) { + if ( request.startsWith( '@wordpress/' ) ) { + return request; + } + }, + } ), + ], }; From 8397d1d9a490fb8080aab4b8aeb9892b4de8e80c Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 22 Dec 2023 12:35:34 +0100 Subject: [PATCH 03/25] Update snapshots --- .../test/__snapshots__/build.js.snap | 245 ++++++++++++++++++ 1 file changed, 245 insertions(+) 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 4240c2f2ea378..3c920c7b29f2d 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,230 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` should produce expected output: Asset file 'assets.php' should match snapshot 1`] = ` +" array('dependencies' => array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '8652d2bf4a1ea1969a6e'), 'fileB.mjs' => array('dependencies' => array(array('id' => '@wordpress/token-list', 'type' => 'dynamic')), 'version' => '17d7d5b2c152592ff3a0')); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, + { + "externalType": "module", + "request": "@wordpress/token-list", + "userRequest": "@wordpress/token-list", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '092c2bce8c247ee11100'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`function-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`has-extension-suffix\` should produce expected output: Asset file 'index.min.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '9b89a3e6236b26559c4e'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`has-extension-suffix\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-default\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array(), 'version' => '34504aa793c63cd3d73a'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-default\` should produce expected output: External modules should match snapshot 1`] = `[]`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-deps\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array(), 'version' => 'e37fbd452a6188261d74'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-deps\` should produce expected output: External modules should match snapshot 1`] = `[]`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-function-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-output-filename\` should produce expected output: Asset file 'main-foo.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":[],"version":"34504aa793c63cd3d73a"}"`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\` should produce expected output: External modules should match snapshot 1`] = `[]`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`overrides\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic'), array('id' => '@wordpress/url', 'type' => 'dynamic'), array('id' => 'rxjs', 'type' => 'dynamic'), array('id' => 'rxjs/operators', 'type' => 'dynamic')), 'version' => '90f2e6327f4e8fb0264f'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`overrides\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, + { + "externalType": "module", + "request": "@wordpress/url", + "userRequest": "@wordpress/url", + }, + { + "externalType": "module", + "request": "rxjs", + "userRequest": "rxjs", + }, + { + "externalType": "module", + "request": "rxjs/operators", + "userRequest": "rxjs/operators", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'a.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => 'aeadada5bf49ae3b9dc2'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'b.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '10df52cc859c01faa91d'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'runtime.asset.php' should match snapshot 1`] = ` +" array(), 'version' => 'd081f44e5ece6763f943'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-imports\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '2d597a618aeebe7ab323'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-imports\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":[{"id":"@wordpress/interactivity","type":"dynamic"}],"version":"d91ead3ebbc3853c802b"}"`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/interactivity", + "userRequest": "@wordpress/interactivity", + }, +] +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-require\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '7a320492a2396d955292'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-require\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/blob", + "userRequest": "@wordpress/blob", + }, +] +`; + exports[`DependencyExtractionWebpackPlugin scripts Webpack \`combine-assets\` should produce expected output: Asset file 'assets.php' should match snapshot 1`] = ` " array('dependencies' => array('lodash', 'wp-blob'), 'version' => 'cbe985cf6e1a25d848e5'), 'fileB.js' => array('dependencies' => array('wp-token-list'), 'version' => '7f3970305cf0aecb54ab')); " @@ -285,6 +510,26 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress\` should ] `; +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash","wp-interactivity"],"version":"ea7590e6d8a8e420f99f"}"`; + +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "window", + "request": "lodash", + "userRequest": "lodash", + }, + { + "externalType": "window", + "request": [ + "wp", + "interactivity", + ], + "userRequest": "@wordpress/interactivity", + }, +] +`; + exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-require\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-blob'), 'version' => '40370eb4ce6428562da6'); " From dce6f57abeec48c6adfb556ffc01211f748f64b6 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 22 Dec 2023 12:35:58 +0100 Subject: [PATCH 04/25] Bundle interactivity This package is not suitable to be used as a script dependency --- packages/dependency-extraction-webpack-plugin/lib/util.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/lib/util.js b/packages/dependency-extraction-webpack-plugin/lib/util.js index 88ab4695d286c..3785534f7a27d 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/util.js +++ b/packages/dependency-extraction-webpack-plugin/lib/util.js @@ -1,10 +1,11 @@ const WORDPRESS_NAMESPACE = '@wordpress/'; const BUNDLED_PACKAGES = [ + '@wordpress/dataviews', '@wordpress/icons', + '@wordpress/interactivity', '@wordpress/interface', - '@wordpress/undo-manager', '@wordpress/sync', - '@wordpress/dataviews', + '@wordpress/undo-manager', ]; /** From 86dfb76e4ce12105bfb570aac275694889e91ecc Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 22 Dec 2023 12:36:24 +0100 Subject: [PATCH 05/25] Update snaps --- .../test/__snapshots__/build.js.snap | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 3c920c7b29f2d..24cf73294b0ba 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -510,10 +510,15 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress\` should ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash","wp-interactivity"],"version":"ea7590e6d8a8e420f99f"}"`; +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash","react","wp-dom-ready"],"version":"8d9fcb3d75d3b475913c"}"`; exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` [ + { + "externalType": "window", + "request": "React", + "userRequest": "react", + }, { "externalType": "window", "request": "lodash", @@ -523,9 +528,9 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interacti "externalType": "window", "request": [ "wp", - "interactivity", + "domReady", ], - "userRequest": "@wordpress/interactivity", + "userRequest": "@wordpress/dom-ready", }, ] `; From afaeceba4631a0b92edc8762d7eacfe26c064156 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 22 Dec 2023 20:12:58 +0100 Subject: [PATCH 06/25] Update README about module handling --- .../README.md | 133 +++++++++++++++++- 1 file changed, 127 insertions(+), 6 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/README.md b/packages/dependency-extraction-webpack-plugin/README.md index eac1e8e27ffc5..6b8942b008674 100644 --- a/packages/dependency-extraction-webpack-plugin/README.md +++ b/packages/dependency-extraction-webpack-plugin/README.md @@ -2,11 +2,15 @@ This webpack plugin serves two purposes: -- Externalize dependencies that are available as script dependencies on modern WordPress sites. -- Add an asset file for each entry point that declares an object with the list of WordPress script dependencies for the entry point. The asset file also contains the current version calculated for the current source code. +- Externalize dependencies that are available as shared scripts or modules on WordPress sites. +- Add an asset file for each entry point that declares an object with the list of WordPress script or module dependencies for the entry point. The asset file also contains the current version calculated for the current source code. This allows JavaScript bundles produced by webpack to leverage WordPress style dependency sharing without an error-prone process of manually maintaining a dependency list. +Version 5 of this plugin adds support for module bundling. [Webpack's `output.module` option](https://webpack.js.org/configuration/output/#outputmodule) should +be used to opt-in to this behavior. This plugin will adapt it's behavior based on the +`output.module` option, producing an asset file suitable for use with the WordPress Module API. + Consult the [webpack website](https://webpack.js.org) for additional information on webpack concepts. ## Installation @@ -17,7 +21,7 @@ Install the module npm install @wordpress/dependency-extraction-webpack-plugin --save-dev ``` -**Note**: This package requires Node.js 14.0.0 or later. It also requires webpack 4.8.3 and newer. It is not compatible with older versions. +**Note**: This package requires Node.js 18.0.0 or later. It also requires webpack 5.0.0 or newer. It is not compatible with older versions. ## Usage @@ -39,7 +43,7 @@ module.exports = { ```js const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); -const config = { +const webpackConfig = { ...defaultConfig, plugins: [ ...defaultConfig.plugins.filter( @@ -56,7 +60,12 @@ const config = { }; ``` -Each entry point in the webpack bundle will include an asset file that declares the WordPress script dependencies that should be enqueued. Such file also contains the unique version hash calculated based on the file content. +### Behavior + +**Note**: This section describes the classic behavior with webpack and WordPress scripts. +For information about usage with modules, jump to the [behavior with modules](#behavior-with-modules) section. + +Each entry point in the webpack bundle will include an asset file that declares the WordPress script dependencies that should be enqueued. This file also contains the unique version hash calculated based on the file content. For example: @@ -88,6 +97,66 @@ By default, the following module requests are handled: This plugin is compatible with `externals`, but they may conflict. For example, adding `{ externals: { '@wordpress/blob': 'wp.blob' } }` to webpack configuration will effectively hide the `@wordpress/blob` module from the plugin and it will not be included in dependency lists. +### Behavior with modules + +This section describes the behavior of this package to bundle ECMAScript modules and generate asset +files suitable for use with the WordPress Module API. + +Some of this plugin's options change, and webpack requires configuration to output modules. Refer to +[webpack's documentation](https://webpack.js.org/configuration/output/#outputmodule) for up-to-date details. + +```js +const webpackConfig = { + ...defaultConfig, + + // These lines are necessary to enable module compilation at time-of-writing: + output: { module: true }, + experiments: { outputModule: true }, + + plugins: [ + ...defaultConfig.plugins.filter( + ( plugin ) => + plugin.constructor.name !== 'DependencyExtractionWebpackPlugin' + ), + new DependencyExtractionWebpackPlugin( { + // With modules, we use `requestToExternalModule`: + requestToExternalModule( request ) { + if ( request === 'my-registered-module' ) { + return request; + } + }, + } ), + ], +}; +``` + +Each entry point in the webpack bundle will include an asset file that declares the WordPress module dependencies that should be enqueued. This file also contains the unique version hash calculated based on the file content. + +For example: + +``` +// Source file entrypoint.js +import { store, getContext } from '@wordpress/interactivity'; + +// Webpack will produce the output output/entrypoint.js +/* bundled JavaScript output */ + +// Webpack will also produce output/entrypoint.asset.php declaring script dependencies + array('@wordpress/interactivity'), 'version' => 'dd4c2dc50d046ed9d4c063a7ca95702f'); +``` + +By default, the following module requests are handled: + +| Request | +| ---------------------------- | +| `@wordpress/interactivity ` | + +(`@wordpress/interactivity` is currently the only available WordPress module.) + +**Note:** This plugin overlaps with the functionality provided by [webpack `externals`](https://webpack.js.org/configuration/externals). This plugin is intended to extract module handles from bundle compilation so that a list of module dependencies does not need to be manually maintained. If you don't need to extract a list of module dependencies, use the `externals` option directly. + +This plugin is compatible with `externals`, but they may conflict. For example, adding `{ externals: { '@wordpress/blob': 'wp.blob' } }` to webpack configuration will effectively hide the `@wordpress/blob` module from the plugin and it will not be included in dependency lists. + #### Options An object can be passed to the constructor to customize the behavior, for example: @@ -119,7 +188,7 @@ The filename for the generated asset file. Accepts the same values as the Webpac - 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. +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 `asset.(json|php)` file generated in the output directory. ##### `combinedOutputFile` @@ -142,6 +211,8 @@ Pass `useDefaults: false` to disable the default request handling. Force `wp-polyfill` to be included in each entry point's dependency list. This would be the same as adding `import '@wordpress/polyfill';` to each entry point. +**Note**: This option is not available with modules. + ##### `externalizedReport` - Type: boolean | string @@ -152,6 +223,8 @@ You can provide a filename, or set it to `true` to report to a default `external ##### `requestToExternal` +**Note**: This option is not available with modules. See [`requestToExternalModule`](#requestToExternalModule) for module usage. + - Type: function `requestToExternal` allows the module handling to be customized. The function should accept a module request string and may return a string representing the global variable to use. An array of strings may be used to access globals via an object path, e.g. `wp.i18n` may be represented as `[ 'wp', 'i18n' ]`. @@ -179,8 +252,43 @@ module.exports = { }; ``` +##### `requestToExternalModule` + +**Note**: This option is only available with modules. See [`requestToExternal`](#requestToExternal) for script usage. + +- Type: function + +`requestToExternalModule` allows the module handling to be customized. The function should accept a module request string and may return a string representing the module to use. Often, the module will have the same name. + +`requestToExternalModule` provided via configuration has precedence over default external handling. Unhandled requests will be handled by the default unless `useDefaults` is set to `false`. + +```js +/** + * Externalize 'my-module' + * + * @param {string} request Requested module + * + * @return {(string|undefined)} Script global + */ +function requestToExternalModule( request ) { + // Handle imports like `import myModule from 'my-module'` + if ( request === 'my-module' ) { + // Import should be ov the form `import { something } from "myModule";` in the final bundle. + return 'myModule'; + } +} + +module.exports = { + plugins: [ + new DependencyExtractionWebpackPlugin( { requestToExternalModule } ), + ], +}; +``` + ##### `requestToHandle` +**Note**: This option is not available with modules. It has no corresponding module configuration. + - Type: function All of the external modules handled by the plugin are expected to be WordPress script dependencies @@ -233,6 +341,19 @@ $script_url = plugins_url( $script_path, __FILE__ ); wp_enqueue_script( 'script', $script_url, $script_asset['dependencies'], $script_asset['version'] ); ``` +Or with modules (the Module API is not yet stable): + +```php +$module_path = 'path/to/script.js'; +$module_asset_path = 'path/to/script.asset.php'; +$module_asset = file_exists( $script_asset_path ) + ? require( $module_asset_path ) + : array( 'dependencies' => array(), 'version' => filemtime( $script_path ) ); +$module_url = plugins_url( $module_path, __FILE__ ); +wp_register_module( 'my-module', $module_url, $module_asset['dependencies'], $module_asset['version'] ); +wp_enqueue_module( 'my-module' ); +``` + ## Contributing to this package This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects. From 7fc14de0954339e91282df2f87fdf167205d1484 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 22 Dec 2023 20:15:05 +0100 Subject: [PATCH 07/25] Add type => module to module asset files --- .../lib/index.js | 4 +++ .../test/__snapshots__/build.js.snap | 34 +++++++++---------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index 333b9c27bbd89..75e99219925d6 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -289,6 +289,10 @@ class DependencyExtractionWebpackPlugin { version: contentHash, }; + if ( this.useModules ) { + assetData.type = 'module'; + } + if ( combineAssets ) { combinedAssetsData[ chunkJSFile ] = assetData; continue; 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 24cf73294b0ba..044f835e7e5c3 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` should produce expected output: Asset file 'assets.php' should match snapshot 1`] = ` -" array('dependencies' => array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '8652d2bf4a1ea1969a6e'), 'fileB.mjs' => array('dependencies' => array(array('id' => '@wordpress/token-list', 'type' => 'dynamic')), 'version' => '17d7d5b2c152592ff3a0')); +" array('dependencies' => array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '8652d2bf4a1ea1969a6e', 'type' => 'module'), 'fileB.mjs' => array('dependencies' => array(array('id' => '@wordpress/token-list', 'type' => 'dynamic')), 'version' => '17d7d5b2c152592ff3a0', 'type' => 'module')); " `; @@ -21,7 +21,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` sh `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '092c2bce8c247ee11100'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '092c2bce8c247ee11100', 'type' => 'module'); " `; @@ -36,7 +36,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` sh `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); " `; @@ -51,7 +51,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`function-output-fil `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`has-extension-suffix\` should produce expected output: Asset file 'index.min.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '9b89a3e6236b26559c4e'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '9b89a3e6236b26559c4e', 'type' => 'module'); " `; @@ -66,21 +66,21 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`has-extension-suffi `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-default\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(), 'version' => '34504aa793c63cd3d73a'); +" array(), 'version' => '34504aa793c63cd3d73a', 'type' => 'module'); " `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-default\` should produce expected output: External modules should match snapshot 1`] = `[]`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-deps\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(), 'version' => 'e37fbd452a6188261d74'); +" array(), 'version' => 'e37fbd452a6188261d74', 'type' => 'module'); " `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-deps\` should produce expected output: External modules should match snapshot 1`] = `[]`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); " `; @@ -95,7 +95,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-function-out `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-output-filename\` should produce expected output: Asset file 'main-foo.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); " `; @@ -109,12 +109,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-output-filen ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":[],"version":"34504aa793c63cd3d73a"}"`; +exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":[],"version":"34504aa793c63cd3d73a","type":"module"}"`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\` should produce expected output: External modules should match snapshot 1`] = `[]`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`overrides\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic'), array('id' => '@wordpress/url', 'type' => 'dynamic'), array('id' => 'rxjs', 'type' => 'dynamic'), array('id' => 'rxjs/operators', 'type' => 'dynamic')), 'version' => '90f2e6327f4e8fb0264f'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic'), array('id' => '@wordpress/url', 'type' => 'dynamic'), array('id' => 'rxjs', 'type' => 'dynamic'), array('id' => 'rxjs/operators', 'type' => 'dynamic')), 'version' => '90f2e6327f4e8fb0264f', 'type' => 'module'); " `; @@ -144,17 +144,17 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`overrides\` should `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'a.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => 'aeadada5bf49ae3b9dc2'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => 'aeadada5bf49ae3b9dc2', 'type' => 'module'); " `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'b.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '10df52cc859c01faa91d'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '10df52cc859c01faa91d', 'type' => 'module'); " `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'runtime.asset.php' should match snapshot 1`] = ` -" array(), 'version' => 'd081f44e5ece6763f943'); +" array(), 'version' => 'd081f44e5ece6763f943', 'type' => 'module'); " `; @@ -169,7 +169,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-singl `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-imports\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '2d597a618aeebe7ab323'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '2d597a618aeebe7ab323', 'type' => 'module'); " `; @@ -184,7 +184,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-imports\` sho `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); " `; @@ -198,7 +198,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":[{"id":"@wordpress/interactivity","type":"dynamic"}],"version":"d91ead3ebbc3853c802b"}"`; +exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":[{"id":"@wordpress/interactivity","type":"dynamic"}],"version":"d91ead3ebbc3853c802b","type":"module"}"`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` [ @@ -211,7 +211,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interacti `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-require\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '7a320492a2396d955292'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '7a320492a2396d955292', 'type' => 'module'); " `; From 72a7dc8d6cf99f8ad783c36df4b7a7b9ac78eaf0 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 22 Dec 2023 20:45:52 +0100 Subject: [PATCH 08/25] Remove commented line --- .../test/fixtures/wordpress-interactivity/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/index.js index aea7641d30ed2..b4dd2f288661e 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/index.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/index.js @@ -5,7 +5,6 @@ import _ from 'lodash'; // This module should be externalized -// import { store } from '@wordpress/interactivity'; const { store, getContext } = await import( '@wordpress/interactivity' ); store( _.identity( 'my-namespace' ), { state: 'is great' } ); From 91dcd126cb59589aacd92b1d214b42c85fcfa504 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 26 Dec 2023 19:49:50 +0100 Subject: [PATCH 09/25] Fix dynamic test --- .../lib/index.js | 18 ++++++---- .../test/__snapshots__/build.js.snap | 36 ++++++++----------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index 75e99219925d6..d93a03c071947 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -225,16 +225,22 @@ class DependencyExtractionWebpackPlugin { const { userRequest } = m; if ( this.externalizedDeps.has( userRequest ) ) { if ( this.useModules ) { - const isDynamic = Array.prototype.every.call( - compilation.moduleGraph.getIncomingConnections( m ), - ( { dependency } ) => { - return ( + let isDynamic = true; + for ( const incomingConnection of compilation.moduleGraph.getIncomingConnections( + m + ) ) { + const { dependency } = incomingConnection; + if ( + ! ( compilation.moduleGraph.getParentBlock( dependency ) instanceof AsyncDependenciesBlock - ); + ) + ) { + isDynamic = false; + break; } - ); + } ( isDynamic ? chunkDynamicDeps : chunkStaticDeps ).add( userRequest 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 044f835e7e5c3..1ad3f32937453 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` should produce expected output: Asset file 'assets.php' should match snapshot 1`] = ` -" array('dependencies' => array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '8652d2bf4a1ea1969a6e', 'type' => 'module'), 'fileB.mjs' => array('dependencies' => array(array('id' => '@wordpress/token-list', 'type' => 'dynamic')), 'version' => '17d7d5b2c152592ff3a0', 'type' => 'module')); +" array('dependencies' => array('@wordpress/blob'), 'version' => '8652d2bf4a1ea1969a6e', 'type' => 'module'), 'fileB.mjs' => array('dependencies' => array('@wordpress/token-list'), 'version' => '17d7d5b2c152592ff3a0', 'type' => 'module')); " `; @@ -21,7 +21,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` sh `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '092c2bce8c247ee11100', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => '092c2bce8c247ee11100', 'type' => 'module'); " `; @@ -36,7 +36,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` sh `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); " `; @@ -51,7 +51,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`function-output-fil `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`has-extension-suffix\` should produce expected output: Asset file 'index.min.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '9b89a3e6236b26559c4e', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => '9b89a3e6236b26559c4e', 'type' => 'module'); " `; @@ -80,7 +80,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-deps\` should pr exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-deps\` should produce expected output: External modules should match snapshot 1`] = `[]`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); " `; @@ -95,7 +95,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-function-out `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-output-filename\` should produce expected output: Asset file 'main-foo.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); " `; @@ -114,7 +114,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\ exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\` should produce expected output: External modules should match snapshot 1`] = `[]`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`overrides\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic'), array('id' => '@wordpress/url', 'type' => 'dynamic'), array('id' => 'rxjs', 'type' => 'dynamic'), array('id' => 'rxjs/operators', 'type' => 'dynamic')), 'version' => '90f2e6327f4e8fb0264f', 'type' => 'module'); +" array('@wordpress/blob', '@wordpress/url', 'rxjs', 'rxjs/operators'), 'version' => '90f2e6327f4e8fb0264f', 'type' => 'module'); " `; @@ -144,12 +144,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`overrides\` should `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'a.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => 'aeadada5bf49ae3b9dc2', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => 'aeadada5bf49ae3b9dc2', 'type' => 'module'); " `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'b.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '10df52cc859c01faa91d', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => '10df52cc859c01faa91d', 'type' => 'module'); " `; @@ -169,7 +169,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-singl `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-imports\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '2d597a618aeebe7ab323', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => '2d597a618aeebe7ab323', 'type' => 'module'); " `; @@ -184,7 +184,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-imports\` sho `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); " `; @@ -198,7 +198,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":[{"id":"@wordpress/interactivity","type":"dynamic"}],"version":"d91ead3ebbc3853c802b","type":"module"}"`; +exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":[{"id":"@wordpress/interactivity","type":"dynamic"}],"version":"f0242eb6da78af6ca4b8","type":"module"}"`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` [ @@ -211,7 +211,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interacti `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-require\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '7a320492a2396d955292', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => '7a320492a2396d955292', 'type' => 'module'); " `; @@ -510,7 +510,7 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress\` should ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash","react","wp-dom-ready"],"version":"8d9fcb3d75d3b475913c"}"`; +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash","react"],"version":"bb0d768bc195f037eb10"}"`; exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` [ @@ -524,14 +524,6 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interacti "request": "lodash", "userRequest": "lodash", }, - { - "externalType": "window", - "request": [ - "wp", - "domReady", - ], - "userRequest": "@wordpress/dom-ready", - }, ] `; From c940791e4e320723011cc71117c697711fd2e7c2 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 26 Dec 2023 20:13:19 +0100 Subject: [PATCH 10/25] Check for matching name Matching the same class is fragile --- packages/dependency-extraction-webpack-plugin/lib/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index d93a03c071947..44eb118f17fad 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -234,7 +234,8 @@ class DependencyExtractionWebpackPlugin { ! ( compilation.moduleGraph.getParentBlock( dependency - ) instanceof AsyncDependenciesBlock + ).constructor.name === + AsyncDependenciesBlock.name ) ) { isDynamic = false; From e0f8cce566b1bbfd6049ed98d699617ad2f15bb2 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 27 Dec 2023 15:31:32 +0100 Subject: [PATCH 11/25] Update CHANGELOG entry --- packages/dependency-extraction-webpack-plugin/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index a3343654ca0e0..f9f6e0c5e544b 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -9,7 +9,7 @@ ### New Features -- The plugin now supports generating ECMAScript modules output **@TODO @sirreal complete this**. +- Add support for producing module-compatible asset files ((#57199)[https://github.com/WordPress/gutenberg/pull/57199]) ## 4.31.0 (2023-12-13) From 10adc238c7657e7cb34c4dff1036ef264ceeb333 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 27 Dec 2023 15:32:20 +0100 Subject: [PATCH 12/25] Fix markdown link syntax --- packages/dependency-extraction-webpack-plugin/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index f9f6e0c5e544b..41a06b6c3beaf 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -9,7 +9,7 @@ ### New Features -- Add support for producing module-compatible asset files ((#57199)[https://github.com/WordPress/gutenberg/pull/57199]) +- Add support for producing module-compatible asset files ([#57199](https://github.com/WordPress/gutenberg/pull/57199)) ## 4.31.0 (2023-12-13) From f456a8374c03c45c0a78b3c0939b604bdd6fc953 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 27 Dec 2023 15:32:53 +0100 Subject: [PATCH 13/25] Missing period --- packages/dependency-extraction-webpack-plugin/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index 41a06b6c3beaf..414a5229117ce 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -9,7 +9,7 @@ ### New Features -- Add support for producing module-compatible asset files ([#57199](https://github.com/WordPress/gutenberg/pull/57199)) +- Add support for producing module-compatible asset files ([#57199](https://github.com/WordPress/gutenberg/pull/57199)). ## 4.31.0 (2023-12-13) From 9d39c86dfaf3adde4dda90255446fb5bf42f0d7f Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 2 Jan 2024 09:56:47 +0100 Subject: [PATCH 14/25] Revert addition of @wordpress/interactivity to bundled scripts --- packages/dependency-extraction-webpack-plugin/lib/util.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dependency-extraction-webpack-plugin/lib/util.js b/packages/dependency-extraction-webpack-plugin/lib/util.js index 3785534f7a27d..bc2b2221e8fc9 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/util.js +++ b/packages/dependency-extraction-webpack-plugin/lib/util.js @@ -2,7 +2,6 @@ const WORDPRESS_NAMESPACE = '@wordpress/'; const BUNDLED_PACKAGES = [ '@wordpress/dataviews', '@wordpress/icons', - '@wordpress/interactivity', '@wordpress/interface', '@wordpress/sync', '@wordpress/undo-manager', From 402f6a1c2db94a16950724f3a1615eec8292813e Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 2 Jan 2024 10:18:23 +0100 Subject: [PATCH 15/25] Update snapshots --- .../test/__snapshots__/build.js.snap | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 1ad3f32937453..911f9a710b4d2 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -510,19 +510,22 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress\` should ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash","react"],"version":"bb0d768bc195f037eb10"}"`; +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash","wp-interactivity"],"version":"b16015e38aea0509f75f"}"`; exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` [ { "externalType": "window", - "request": "React", - "userRequest": "react", + "request": "lodash", + "userRequest": "lodash", }, { "externalType": "window", - "request": "lodash", - "userRequest": "lodash", + "request": [ + "wp", + "interactivity", + ], + "userRequest": "@wordpress/interactivity", }, ] `; From 8cdbfd99002b82478b166167ff7583db9b75a1ab Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 3 Jan 2024 17:59:55 +0100 Subject: [PATCH 16/25] Apply README suggestions, add experimental warning --- packages/dependency-extraction-webpack-plugin/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/README.md b/packages/dependency-extraction-webpack-plugin/README.md index 6b8942b008674..d3f1c7e06bd88 100644 --- a/packages/dependency-extraction-webpack-plugin/README.md +++ b/packages/dependency-extraction-webpack-plugin/README.md @@ -60,10 +60,7 @@ const webpackConfig = { }; ``` -### Behavior - -**Note**: This section describes the classic behavior with webpack and WordPress scripts. -For information about usage with modules, jump to the [behavior with modules](#behavior-with-modules) section. +### Behavior with scripts Each entry point in the webpack bundle will include an asset file that declares the WordPress script dependencies that should be enqueued. This file also contains the unique version hash calculated based on the file content. @@ -99,8 +96,10 @@ This plugin is compatible with `externals`, but they may conflict. For example, ### Behavior with modules +**Warning:** Modules support is considered experimental at this time. + This section describes the behavior of this package to bundle ECMAScript modules and generate asset -files suitable for use with the WordPress Module API. +files suitable for use with the WordPress Modules API. Some of this plugin's options change, and webpack requires configuration to output modules. Refer to [webpack's documentation](https://webpack.js.org/configuration/output/#outputmodule) for up-to-date details. From 53822c6042816457be621623729ac65c0915c6e4 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 3 Jan 2024 18:00:34 +0100 Subject: [PATCH 17/25] Fix README modules PHP example Co-authored-by: Luis Herranz --- packages/dependency-extraction-webpack-plugin/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/README.md b/packages/dependency-extraction-webpack-plugin/README.md index d3f1c7e06bd88..2e6633a15a679 100644 --- a/packages/dependency-extraction-webpack-plugin/README.md +++ b/packages/dependency-extraction-webpack-plugin/README.md @@ -343,11 +343,11 @@ wp_enqueue_script( 'script', $script_url, $script_asset['dependencies'], $script Or with modules (the Module API is not yet stable): ```php -$module_path = 'path/to/script.js'; -$module_asset_path = 'path/to/script.asset.php'; -$module_asset = file_exists( $script_asset_path ) +$module_path = 'path/to/module.js'; +$module_asset_path = 'path/to/module.asset.php'; +$module_asset = file_exists( $module_asset_path ) ? require( $module_asset_path ) - : array( 'dependencies' => array(), 'version' => filemtime( $script_path ) ); + : array( 'dependencies' => array(), 'version' => filemtime( $module_path ) ); $module_url = plugins_url( $module_path, __FILE__ ); wp_register_module( 'my-module', $module_url, $module_asset['dependencies'], $module_asset['version'] ); wp_enqueue_module( 'my-module' ); From 1b4800cc5d871702920ea1979cf42ebe994a5666 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 3 Jan 2024 19:51:20 +0100 Subject: [PATCH 18/25] Add name to webpack config for easier test debugging --- packages/dependency-extraction-webpack-plugin/test/build.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dependency-extraction-webpack-plugin/test/build.js b/packages/dependency-extraction-webpack-plugin/test/build.js index 8a9c607eb5378..3b29d55caf2bb 100644 --- a/packages/dependency-extraction-webpack-plugin/test/build.js +++ b/packages/dependency-extraction-webpack-plugin/test/build.js @@ -36,6 +36,7 @@ describe.each( /** @type {const} */ ( [ 'scripts', 'modules' ] ) )( test( 'should produce expected output', async () => { const options = Object.assign( { + name: `${ configCase }-${ moduleMode }`, target: 'web', context: testDirectory, entry: './index.js', From 4be92b98e04240d3f854f9667647434d4b32beff Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 3 Jan 2024 19:39:40 +0100 Subject: [PATCH 19/25] Search for a static path to root to test for dynamic --- .../lib/index.js | 81 ++++++++++++++----- .../wordpress-interactivity/webpack.config.js | 11 +-- 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index 44eb118f17fad..01545c002be22 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -221,29 +221,20 @@ class DependencyExtractionWebpackPlugin { chunkStaticDeps.add( 'wp-polyfill' ); } + /** + * @param {webpack.Module} m + */ const processModule = ( m ) => { const { userRequest } = m; if ( this.externalizedDeps.has( userRequest ) ) { if ( this.useModules ) { - let isDynamic = true; - for ( const incomingConnection of compilation.moduleGraph.getIncomingConnections( - m - ) ) { - const { dependency } = incomingConnection; - if ( - ! ( - compilation.moduleGraph.getParentBlock( - dependency - ).constructor.name === - AsyncDependenciesBlock.name - ) - ) { - isDynamic = false; - break; - } - } - - ( isDynamic ? chunkDynamicDeps : chunkStaticDeps ).add( + const isStatic = + DependencyExtractionWebpackPlugin.hasStaticDependencyPathToRoot( + compilation, + m + ); + + ( isStatic ? chunkStaticDeps : chunkDynamicDeps ).add( userRequest ); } else { @@ -346,6 +337,58 @@ class DependencyExtractionWebpackPlugin { ); } } + + /** + * Can we trace a line of static dependencies from an entry to a module + * + * @param {webpack.Compilation} compilation + * @param {webpack.DependenciesBlock} block + * + * @return {boolean} True if there is a static import path to the root + */ + static hasStaticDependencyPathToRoot( compilation, block ) { + const incomingConnections = [ + ...compilation.moduleGraph.getIncomingConnections( block ), + ].filter( + ( connection ) => + // Library connections don't have a dependency, this is a root + connection.dependency && + // Entry dependencies are another root + connection.dependency.constructor.name !== 'EntryDependency' + ); + + // If we don't have non-entry, non-library incoming connections, + // we've reached a root of + if ( ! incomingConnections.length ) { + return true; + } + + const staticDependentModules = incomingConnections.flatMap( + ( connection ) => { + const { dependency } = connection; + const parentBlock = + compilation.moduleGraph.getParentBlock( dependency ); + + return parentBlock.constructor.name !== + AsyncDependenciesBlock.name + ? [ compilation.moduleGraph.getParentModule( dependency ) ] + : []; + } + ); + + // All the dependencies were Async, the module was reached via a dynamic import + if ( ! staticDependentModules.length ) { + return false; + } + + // Continue to explore any static dependencies + return staticDependentModules.some( ( parentStaticDependentModule ) => + DependencyExtractionWebpackPlugin.hasStaticDependencyPathToRoot( + compilation, + parentStaticDependentModule + ) + ); + } } module.exports = DependencyExtractionWebpackPlugin; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/webpack.config.js index 04401e3aef989..bfffff3ae7831 100644 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/webpack.config.js +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/webpack.config.js @@ -4,14 +4,5 @@ const DependencyExtractionWebpackPlugin = require( '../../..' ); module.exports = { - plugins: [ - new DependencyExtractionWebpackPlugin( { - outputFormat: 'json', - requestToExternalModule( request ) { - if ( request.startsWith( 'test-external' ) ) { - return request; - } - }, - } ), - ], + plugins: [ new DependencyExtractionWebpackPlugin() ], }; From c626a958b34e5c9d5372a5e62c15300661080b84 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 4 Jan 2024 12:49:03 +0100 Subject: [PATCH 20/25] Update snapshots --- .../test/__snapshots__/build.js.snap | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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 911f9a710b4d2..f0250d864e472 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -21,7 +21,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` sh `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` -" array('@wordpress/blob'), 'version' => '092c2bce8c247ee11100', 'type' => 'module'); +" array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '092c2bce8c247ee11100', 'type' => 'module'); " `; @@ -198,7 +198,10 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":[{"id":"@wordpress/interactivity","type":"dynamic"}],"version":"f0242eb6da78af6ca4b8","type":"module"}"`; +exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/interactivity', 'type' => 'dynamic')), 'version' => 'f0242eb6da78af6ca4b8', 'type' => 'module'); +" +`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` [ @@ -510,7 +513,10 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress\` should ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash","wp-interactivity"],"version":"b16015e38aea0509f75f"}"`; +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array('lodash', 'wp-interactivity'), 'version' => 'b16015e38aea0509f75f'); +" +`; exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` [ From a857876143417ea7bc8d07374fa59e2e1f5a87b6 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 4 Jan 2024 13:08:34 +0100 Subject: [PATCH 21/25] Add cyclic static dep test --- .../test/__snapshots__/build.js.snap | 33 +++++++++++++++++++ .../fixtures/cyclic-dependency-graph/a.js | 13 ++++++++ .../fixtures/cyclic-dependency-graph/b.js | 10 ++++++ .../fixtures/cyclic-dependency-graph/index.js | 8 +++++ .../cyclic-dependency-graph/webpack.config.js | 8 +++++ 5 files changed, 72 insertions(+) create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/a.js create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/b.js create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/index.js create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/webpack.config.js 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 f0250d864e472..80eab8e7033a8 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -20,6 +20,21 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` sh ] `; +exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dependency-graph\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array('@wordpress/interactivity'), 'version' => '58fadee5eca3ad30aff6', 'type' => 'module'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dependency-graph\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/interactivity", + "userRequest": "@wordpress/interactivity", + }, +] +`; + exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` " array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '092c2bce8c247ee11100', 'type' => 'module'); " @@ -259,6 +274,24 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`combine-assets\` sh ] `; +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dependency-graph\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array('wp-interactivity'), 'version' => '79a1af3afac581f52492'); +" +`; + +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dependency-graph\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "window", + "request": [ + "wp", + "interactivity", + ], + "userRequest": "@wordpress/interactivity", + }, +] +`; + exports[`DependencyExtractionWebpackPlugin scripts Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-blob'), 'version' => 'c0e8a6f22065ea096606'); " diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/a.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/a.js new file mode 100644 index 0000000000000..11dd7764ad5f9 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/a.js @@ -0,0 +1,13 @@ +/** + * WordPress dependencies + */ +import { store } from '@wordpress/interactivity'; + +/** + * Internal dependencies + */ +import { identity } from './b.js'; + +identity( 1 ); + +export { identity, store }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/b.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/b.js new file mode 100644 index 0000000000000..ce109acccbd37 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/b.js @@ -0,0 +1,10 @@ +/** + * Internal dependencies + */ +import { store } from './a.js'; + +export function identity( x ) { + return x; +} + +export { store }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/index.js new file mode 100644 index 0000000000000..13b17a73ad4af --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/index.js @@ -0,0 +1,8 @@ +/** + * Internal dependencies + */ +import { identity as aIdentity, store as aStore } from './a.js'; +import { identity as bIdentity, store as bStore } from './b.js'; + +aStore( aIdentity( 'a' ), { a: aIdentity( 'a' ) } ); +bStore( bIdentity( 'b' ), { b: bIdentity( 'b' ) } ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/webpack.config.js new file mode 100644 index 0000000000000..bfffff3ae7831 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dependency-graph/webpack.config.js @@ -0,0 +1,8 @@ +/** + * Internal dependencies + */ +const DependencyExtractionWebpackPlugin = require( '../../..' ); + +module.exports = { + plugins: [ new DependencyExtractionWebpackPlugin() ], +}; From 0b094345fcc7875c8504dcd4d0829e75ef6981c7 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 4 Jan 2024 13:13:33 +0100 Subject: [PATCH 22/25] Add cyclic dynamic test --- .../test/__snapshots__/build.js.snap | 33 +++++++++++++++++++ .../cyclic-dynamic-dependency-graph/a.js | 13 ++++++++ .../cyclic-dynamic-dependency-graph/b.js | 10 ++++++ .../cyclic-dynamic-dependency-graph/index.js | 9 +++++ .../webpack.config.js | 8 +++++ 5 files changed, 73 insertions(+) create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/a.js create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/b.js create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/index.js create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/webpack.config.js 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 80eab8e7033a8..765f20e05f1b9 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -35,6 +35,21 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dependency-g ] `; +exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dynamic-dependency-graph\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array(array('id' => '@wordpress/interactivity', 'type' => 'dynamic')), 'version' => '293aebad4ca761cf396f', 'type' => 'module'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dynamic-dependency-graph\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "@wordpress/interactivity", + "userRequest": "@wordpress/interactivity", + }, +] +`; + exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` " array(array('id' => '@wordpress/blob', 'type' => 'dynamic')), 'version' => '092c2bce8c247ee11100', 'type' => 'module'); " @@ -292,6 +307,24 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dependency-g ] `; +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dynamic-dependency-graph\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array('wp-interactivity'), 'version' => 'ac0e2f1bcd3a6a0e7aff'); +" +`; + +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dynamic-dependency-graph\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "window", + "request": [ + "wp", + "interactivity", + ], + "userRequest": "@wordpress/interactivity", + }, +] +`; + exports[`DependencyExtractionWebpackPlugin scripts Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-blob'), 'version' => 'c0e8a6f22065ea096606'); " diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/a.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/a.js new file mode 100644 index 0000000000000..11dd7764ad5f9 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/a.js @@ -0,0 +1,13 @@ +/** + * WordPress dependencies + */ +import { store } from '@wordpress/interactivity'; + +/** + * Internal dependencies + */ +import { identity } from './b.js'; + +identity( 1 ); + +export { identity, store }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/b.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/b.js new file mode 100644 index 0000000000000..25a6aa127d26f --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/b.js @@ -0,0 +1,10 @@ +/** + * Internal dependencies + */ +const { store } = import( './a.js' ); + +export function identity( x ) { + return x; +} + +export { store }; diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/index.js new file mode 100644 index 0000000000000..073b4244dcea2 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/index.js @@ -0,0 +1,9 @@ +/** + * Internal dependencies + */ +import { identity as bIdentity, store as bStore } from './b.js'; + +const { identity: aIdentity, store: aStore } = await import( './a.js' ); + +aStore( aIdentity( 'a' ), { a: aIdentity( 'a' ) } ); +bStore( bIdentity( 'b' ), { b: bIdentity( 'b' ) } ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/webpack.config.js new file mode 100644 index 0000000000000..bfffff3ae7831 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/cyclic-dynamic-dependency-graph/webpack.config.js @@ -0,0 +1,8 @@ +/** + * Internal dependencies + */ +const DependencyExtractionWebpackPlugin = require( '../../..' ); + +module.exports = { + plugins: [ new DependencyExtractionWebpackPlugin() ], +}; From 75e1443e7bb974726697e0a6ca53a902d0c500c2 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 4 Jan 2024 15:53:48 +0100 Subject: [PATCH 23/25] Add test for renamed modules, fix renamed module bug --- .../lib/index.js | 2 +- .../test/__snapshots__/build.js.snap | 46 +++++++++++++++++++ .../test/fixtures/module-renames/index.js | 7 +++ .../fixtures/module-renames/webpack.config.js | 32 +++++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/module-renames/index.js create mode 100644 packages/dependency-extraction-webpack-plugin/test/fixtures/module-renames/webpack.config.js diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index 01545c002be22..46954d64186b9 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -235,7 +235,7 @@ class DependencyExtractionWebpackPlugin { ); ( isStatic ? chunkStaticDeps : chunkDynamicDeps ).add( - userRequest + m.request ); } else { chunkStaticDeps.add( 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 765f20e05f1b9..3c8f89fc14ee9 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -95,6 +95,26 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`has-extension-suffi ] `; +exports[`DependencyExtractionWebpackPlugin modules Webpack \`module-renames\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array('renamed--@my/module', 'renamed--other-module'), 'version' => '601cf94eb9a182fcc0ed', 'type' => 'module'); +" +`; + +exports[`DependencyExtractionWebpackPlugin modules Webpack \`module-renames\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "module", + "request": "renamed--@my/module", + "userRequest": "@my/module", + }, + { + "externalType": "module", + "request": "renamed--other-module", + "userRequest": "other-module", + }, +] +`; + exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-default\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` " array(), 'version' => '34504aa793c63cd3d73a', 'type' => 'module'); " @@ -389,6 +409,32 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`has-extension-suffi ] `; +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`module-renames\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +" array('renamed--@my/module', 'renamed--other-module'), 'version' => '34854902f36ec8e176d6'); +" +`; + +exports[`DependencyExtractionWebpackPlugin scripts Webpack \`module-renames\` should produce expected output: External modules should match snapshot 1`] = ` +[ + { + "externalType": "window", + "request": [ + "my-namespace", + "renamed--@my/module", + ], + "userRequest": "@my/module", + }, + { + "externalType": "window", + "request": [ + "my-namespace", + "renamed--other-module", + ], + "userRequest": "other-module", + }, +] +`; + exports[`DependencyExtractionWebpackPlugin scripts Webpack \`no-default\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` " array(), 'version' => '43880e6c42e7c39fcdf1'); " diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/module-renames/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/module-renames/index.js new file mode 100644 index 0000000000000..dc3702922c6ff --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/module-renames/index.js @@ -0,0 +1,7 @@ +/** + * External dependencies + */ +import * as m from '@my/module'; +import { other } from 'other-module'; + +m.load( other ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/module-renames/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/module-renames/webpack.config.js new file mode 100644 index 0000000000000..8b78e1fdea150 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/module-renames/webpack.config.js @@ -0,0 +1,32 @@ +/** + * Internal dependencies + */ +const DependencyExtractionWebpackPlugin = require( '../../..' ); + +module.exports = { + plugins: [ + new DependencyExtractionWebpackPlugin( { + requestToExternal( request ) { + switch ( request ) { + case '@my/module': + case 'other-module': + return [ 'my-namespace', `renamed--${ request }` ]; + } + }, + requestToHandle( request ) { + switch ( request ) { + case '@my/module': + case 'other-module': + return `renamed--${ request }`; + } + }, + requestToExternalModule( request ) { + switch ( request ) { + case '@my/module': + case 'other-module': + return `renamed--${ request }`; + } + }, + } ), + ], +}; From 5eac6862e0bdb0cbf892a787a419fd8139176734 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 4 Jan 2024 15:56:53 +0100 Subject: [PATCH 24/25] Move ExternalsPlugin instantiation to apply method Avoid instantiating with one externals type and switching later --- .../lib/index.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index 46954d64186b9..400ea39c02293 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -53,14 +53,6 @@ class DependencyExtractionWebpackPlugin { * @type {boolean} */ this.useModules = false; - - /** - * Offload externalization work to the ExternalsPlugin. - */ - this.externalsPlugin = new webpack.ExternalsPlugin( - 'window', - this.externalizeWpDeps.bind( this ) - ); } /** @@ -142,9 +134,14 @@ class DependencyExtractionWebpackPlugin { apply( compiler ) { this.useModules = Boolean( compiler.options.output?.module ); - if ( this.useModules ) { - this.externalsPlugin.type = 'module'; - } + /** + * Offload externalization work to the ExternalsPlugin. + * @type {webpack.ExternalsPlugin} + */ + this.externalsPlugin = new webpack.ExternalsPlugin( + this.useModules ? 'module' : 'window', + this.externalizeWpDeps.bind( this ) + ); this.externalsPlugin.apply( compiler ); From c1098aeff7da270a90a45a1db570ae138b21d5fb Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 4 Jan 2024 16:47:43 +0100 Subject: [PATCH 25/25] This is supposed to say "assets" --- packages/dependency-extraction-webpack-plugin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dependency-extraction-webpack-plugin/README.md b/packages/dependency-extraction-webpack-plugin/README.md index 2e6633a15a679..91fb36e8ad09d 100644 --- a/packages/dependency-extraction-webpack-plugin/README.md +++ b/packages/dependency-extraction-webpack-plugin/README.md @@ -187,7 +187,7 @@ The filename for the generated asset file. Accepts the same values as the Webpac - 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 `asset.(json|php)` file generated in the output directory. +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. ##### `combinedOutputFile`