diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index 78de3e4142c87..414a5229117ce 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 + +- Add support for producing module-compatible asset files ([#57199](https://github.com/WordPress/gutenberg/pull/57199)). ## 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. diff --git a/packages/dependency-extraction-webpack-plugin/README.md b/packages/dependency-extraction-webpack-plugin/README.md index eac1e8e27ffc5..91fb36e8ad09d 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,9 @@ 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 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. For example: @@ -88,6 +94,68 @@ 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 + +**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 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. + +```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: @@ -142,6 +210,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 +222,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 +251,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 +340,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/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( $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' ); +``` + ## 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. 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..400ea39c02293 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,40 @@ 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. - this.externalsPlugin = new webpack.ExternalsPlugin( - 'window', - this.externalizeWpDeps.bind( this ) - ); + /** + * Should we use modules. This will be set later to match webpack's + * output.module option. + * + * @type {boolean} + */ + this.useModules = false; } + /** + * @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 +77,9 @@ class DependencyExtractionWebpackPlugin { typeof externalRequest === 'undefined' && this.options.useDefaults ) { - externalRequest = defaultRequestToExternal( request ); + externalRequest = this.useModules + ? defaultRequestToExternalModule( request ) + : defaultRequestToExternal( request ); } if ( externalRequest ) { @@ -73,6 +91,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 +116,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 } ) => { + /** + * @param {webpack.Module} m + */ + const processModule = ( m ) => { + const { userRequest } = m; if ( this.externalizedDeps.has( userRequest ) ) { - chunkDeps.add( this.mapRequestToDependency( userRequest ) ); + if ( this.useModules ) { + const isStatic = + DependencyExtractionWebpackPlugin.hasStaticDependencyPathToRoot( + compilation, + m + ); + + ( isStatic ? chunkStaticDeps : chunkDynamicDeps ).add( + m.request + ); + } 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,11 +274,20 @@ 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, }; + if ( this.useModules ) { + assetData.type = 'module'; + } + if ( combineAssets ) { combinedAssetsData[ chunkJSFile ] = assetData; continue; @@ -231,7 +305,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. @@ -260,6 +334,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/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..bc2b2221e8fc9 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/util.js +++ b/packages/dependency-extraction-webpack-plugin/lib/util.js @@ -1,10 +1,10 @@ const WORDPRESS_NAMESPACE = '@wordpress/'; const BUNDLED_PACKAGES = [ + '@wordpress/dataviews', '@wordpress/icons', '@wordpress/interface', - '@wordpress/undo-manager', '@wordpress/sync', - '@wordpress/dataviews', + '@wordpress/undo-manager', ]; /** @@ -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/__snapshots__/build.js.snap b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap index 4240c2f2ea378..3c8f89fc14ee9 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,283 @@ // 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('@wordpress/blob'), 'version' => '8652d2bf4a1ea1969a6e', 'type' => 'module'), 'fileB.mjs' => array('dependencies' => array('@wordpress/token-list'), 'version' => '17d7d5b2c152592ff3a0', 'type' => 'module')); +" +`; + +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 \`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 \`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'); +" +`; + +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('@wordpress/blob'), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); +" +`; + +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('@wordpress/blob'), 'version' => '9b89a3e6236b26559c4e', 'type' => 'module'); +" +`; + +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 \`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'); +" +`; + +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', '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('@wordpress/blob'), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); +" +`; + +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('@wordpress/blob'), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); +" +`; + +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","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('@wordpress/blob', '@wordpress/url', 'rxjs', 'rxjs/operators'), 'version' => '90f2e6327f4e8fb0264f', 'type' => 'module'); +" +`; + +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('@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('@wordpress/blob'), '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', 'type' => 'module'); +" +`; + +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('@wordpress/blob'), 'version' => '2d597a618aeebe7ab323', 'type' => 'module'); +" +`; + +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('@wordpress/blob'), 'version' => '5207bcd3fdd29de25f37', 'type' => 'module'); +" +`; + +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.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`] = ` +[ + { + "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('@wordpress/blob'), 'version' => '7a320492a2396d955292', 'type' => 'module'); +" +`; + +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')); " @@ -31,6 +309,42 @@ 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 \`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'); " @@ -95,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'); " @@ -285,6 +625,29 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress\` should ] `; +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`] = ` +[ + { + "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'); " diff --git a/packages/dependency-extraction-webpack-plugin/test/build.js b/packages/dependency-extraction-webpack-plugin/test/build.js index 7c84e4b0bcc8b..3b29d55caf2bb 100644 --- a/packages/dependency-extraction-webpack-plugin/test/build.js +++ b/packages/dependency-extraction-webpack-plugin/test/build.js @@ -13,89 +13,109 @@ 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( + { + name: `${ configCase }-${ moduleMode }`, + 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/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() ], +}; 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() ], +}; 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/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 }`; + } + }, + } ), + ], +}; 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..b4dd2f288661e --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/index.js @@ -0,0 +1,12 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +import _ from 'lodash'; + +// This module should be externalized +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..bfffff3ae7831 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/wordpress-interactivity/webpack.config.js @@ -0,0 +1,8 @@ +/** + * Internal dependencies + */ +const DependencyExtractionWebpackPlugin = require( '../../..' ); + +module.exports = { + plugins: [ new DependencyExtractionWebpackPlugin() ], +}; 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; + } + }, + } ), + ], };