Skip to content

Commit

Permalink
Fix dependency extraction webpack v5 deprecation (#33090)
Browse files Browse the repository at this point in the history
* Get rid of webpack v5 deprecation supporting also v4

Resolve deprecations using conditionals checks to implement solutions for both versions

* Little nitpick

Access webpack compilation property trough compiler instead

* Fix code indentation and format

* Shorthand version of runtime files push/add
  • Loading branch information
ismaeldcom committed Jul 19, 2021
1 parent dce71ea commit c695c3b
Showing 1 changed file with 128 additions and 101 deletions.
229 changes: 128 additions & 101 deletions packages/dependency-extraction-webpack-plugin/lib/index.js
Expand Up @@ -7,6 +7,7 @@ const webpack = require( 'webpack' );
// In webpack 5 there is a `webpack.sources` field but for webpack 4 we have to fallback to the `webpack-sources` package.
const { RawSource } = webpack.sources || require( 'webpack-sources' );
const json2php = require( 'json2php' );
const isWebpack4 = webpack.version.startsWith( '4.' );

/**
* Internal dependencies
Expand Down Expand Up @@ -41,7 +42,9 @@ class DependencyExtractionWebpackPlugin {
// Offload externalization work to the ExternalsPlugin.
this.externalsPlugin = new webpack.ExternalsPlugin(
'window',
this.externalizeWpDeps.bind( this )
isWebpack4
? this.externalizeWpDeps.bind( this )
: this.externalizeWpDepsV5.bind( this )
);
}

Expand Down Expand Up @@ -70,6 +73,10 @@ class DependencyExtractionWebpackPlugin {
return callback();
}

externalizeWpDepsV5( { context, request }, callback ) {
return this.externalizeWpDeps( context, request, callback );
}

mapRequestToDependency( request ) {
// Handle via options.requestToHandle first
if ( typeof this.options.requestToHandle === 'function' ) {
Expand Down Expand Up @@ -104,116 +111,136 @@ class DependencyExtractionWebpackPlugin {
apply( compiler ) {
this.externalsPlugin.apply( compiler );

compiler.hooks.emit.tap( this.constructor.name, ( compilation ) => {
const {
combineAssets,
combinedOutputFile,
injectPolyfill,
outputFormat,
} = this.options;

const combinedAssetsData = {};

// Process each entry point independently.
for ( const [
entrypointName,
entrypoint,
] of compilation.entrypoints.entries() ) {
const entrypointExternalizedWpDeps = new Set();
if ( injectPolyfill ) {
entrypointExternalizedWpDeps.add( 'wp-polyfill' );
if ( isWebpack4 ) {
compiler.hooks.emit.tap( this.constructor.name, ( compilation ) =>
this.addAssets( compilation, compiler )
);
} else {
compiler.hooks.thisCompilation.tap(
this.constructor.name,
( compilation ) => {
compilation.hooks.processAssets.tap(
{
name: this.constructor.name,
stage:
compiler.webpack.Compilation
.PROCESS_ASSETS_STAGE_ADDITIONAL,
},
() => this.addAssets( compilation, compiler )
);
}
);
}
}

const processModule = ( { userRequest } ) => {
if ( this.externalizedDeps.has( userRequest ) ) {
const scriptDependency = this.mapRequestToDependency(
userRequest
);
entrypointExternalizedWpDeps.add( scriptDependency );
}
};

// Search for externalized modules in all chunks.
for ( const chunk of entrypoint.chunks ) {
for ( const chunkModule of chunk.modulesIterable ) {
processModule( chunkModule );
// loop through submodules of ConcatenatedModule
if ( chunkModule.modules ) {
for ( const concatModule of chunkModule.modules ) {
processModule( concatModule );
}
addAssets( compilation, compiler ) {
const {
combineAssets,
combinedOutputFile,
injectPolyfill,
outputFormat,
} = this.options;

const combinedAssetsData = {};

// Process each entry point independently.
for ( const [
entrypointName,
entrypoint,
] of compilation.entrypoints.entries() ) {
const entrypointExternalizedWpDeps = new Set();
if ( injectPolyfill ) {
entrypointExternalizedWpDeps.add( 'wp-polyfill' );
}

const processModule = ( { userRequest } ) => {
if ( this.externalizedDeps.has( userRequest ) ) {
const scriptDependency = this.mapRequestToDependency(
userRequest
);
entrypointExternalizedWpDeps.add( scriptDependency );
}
};

// Search for externalized modules in all chunks.
for ( const chunk of entrypoint.chunks ) {
const modulesIterable = isWebpack4
? chunk.modulesIterable
: compilation.chunkGraph.getChunkModules( chunk );
for ( const chunkModule of modulesIterable ) {
processModule( chunkModule );
// loop through submodules of ConcatenatedModule
if ( chunkModule.modules ) {
for ( const concatModule of chunkModule.modules ) {
processModule( concatModule );
}
}
}
}

const runtimeChunk = entrypoint.getRuntimeChunk();

const assetData = {
// Get a sorted array so we can produce a stable, stringified representation.
dependencies: Array.from(
entrypointExternalizedWpDeps
).sort(),
version: runtimeChunk.hash,
};

const assetString = this.stringify( assetData );

// Determine a filename for the asset file.
const [ filename, query ] = entrypointName.split( '?', 2 );
const buildFilename = compilation.getPath(
compiler.options.output.filename,
{
chunk: runtimeChunk,
filename,
query,
basename: basename( filename ),
contentHash: createHash( 'md4' )
.update( assetString )
.digest( 'hex' ),
}
);

if ( combineAssets ) {
combinedAssetsData[ buildFilename ] = assetData;
continue;
const runtimeChunk = entrypoint.getRuntimeChunk();

const assetData = {
// Get a sorted array so we can produce a stable, stringified representation.
dependencies: Array.from( entrypointExternalizedWpDeps ).sort(),
version: runtimeChunk.hash,
};

const assetString = this.stringify( assetData );

// Determine a filename for the asset file.
const [ filename, query ] = entrypointName.split( '?', 2 );
const buildFilename = compilation.getPath(
compiler.options.output.filename,
{
chunk: runtimeChunk,
filename,
query,
basename: basename( filename ),
contentHash: createHash( 'md4' )
.update( assetString )
.digest( 'hex' ),
}

const assetFilename = buildFilename.replace(
/\.js$/i,
'.asset.' + ( outputFormat === 'php' ? 'php' : 'json' )
);

// Add source and file into compilation for webpack to output.
compilation.assets[ assetFilename ] = new RawSource(
assetString
);
runtimeChunk.files.push( assetFilename );
}
);

if ( combineAssets ) {
// Assert the `string` type for output path.
// The type indicates the option may be `undefined`.
// However, at this point in compilation, webpack has filled the options in if
// they were not provided.
const outputFolder = /** @type {{path:string}} */ ( compiler
.options.output ).path;

const assetsFilePath = path.resolve(
outputFolder,
combinedOutputFile ||
'assets.' + ( outputFormat === 'php' ? 'php' : 'json' )
);
const assetsFilename = path.relative(
outputFolder,
assetsFilePath
);

// Add source into compilation for webpack to output.
compilation.assets[ assetsFilename ] = new RawSource(
this.stringify( combinedAssetsData )
);
combinedAssetsData[ buildFilename ] = assetData;
continue;
}
} );

const assetFilename = buildFilename.replace(
/\.js$/i,
'.asset.' + ( outputFormat === 'php' ? 'php' : 'json' )
);

// Add source and file into compilation for webpack to output.
compilation.assets[ assetFilename ] = new RawSource( assetString );
runtimeChunk.files[ isWebpack4 ? 'push' : 'add' ]( assetFilename );
}

if ( combineAssets ) {
// Assert the `string` type for output path.
// The type indicates the option may be `undefined`.
// However, at this point in compilation, webpack has filled the options in if
// they were not provided.
const outputFolder = /** @type {{path:string}} */ ( compiler.options
.output ).path;

const assetsFilePath = path.resolve(
outputFolder,
combinedOutputFile ||
'assets.' + ( outputFormat === 'php' ? 'php' : 'json' )
);
const assetsFilename = path.relative(
outputFolder,
assetsFilePath
);

// Add source into compilation for webpack to output.
compilation.assets[ assetsFilename ] = new RawSource(
this.stringify( combinedAssetsData )
);
}
}
}

Expand Down

0 comments on commit c695c3b

Please sign in to comment.