block_editor-1.0.x-dev/tools/webpack/wordpress-packages.js
tools/webpack/wordpress-packages.js
/**
* External dependencies
*/
const { join } = require( 'path' );
const { resolve } = require( 'path' );
/**
* Internal dependencies
*/
const packageJson = require( '../../package.json' );
// WordPress packages namespace
const WORDPRESS_NAMESPACE = '@wordpress/';
/**
* Dynamically extract dependencies from a WordPress package's package.json
*
* @param {string} packageName Package name without @wordpress/ prefix
* @return {Array} Array of dependency handles (react, react-dom, wp-*)
*/
function extractPackageDependencies( packageName ) {
try {
const pkgPath = resolve( __dirname, '..', '..', 'node_modules', '@wordpress', packageName );
const pkgJson = require( join( pkgPath, 'package.json' ) );
const deps = [];
// Get all dependencies and peerDependencies
const allDeps = {
...( pkgJson.dependencies || {} ),
...( pkgJson.peerDependencies || {} ),
};
// Extract relevant dependencies
Object.keys( allDeps ).forEach( ( dep ) => {
if ( dep === 'react' ) {
deps.push( 'react' );
} else if ( dep === 'react-dom' ) {
deps.push( 'react-dom' );
} else if ( dep.startsWith( '@wordpress/' ) ) {
// Convert @wordpress/element to wp-element
const wpPkg = dep.replace( '@wordpress/', '' );
deps.push( `wp-${ wpPkg }` );
}
} );
return deps;
} catch ( error ) {
console.warn( `⚠️ Could not extract dependencies for @wordpress/${ packageName }:`, error.message );
return [];
}
}
/**
* Custom plugin to create asset files with dynamically extracted dependencies
*/
class WordPressAssetPlugin {
apply( compiler ) {
compiler.hooks.emit.tapAsync( 'WordPressAssetPlugin', ( compilation, callback ) => {
const packages = getWordPressPackages();
packages.forEach( ( pkg ) => {
// Dynamically extract dependencies from package.json
const deps = extractPackageDependencies( pkg );
// Get the chunk hash for versioning
const chunks = Array.from( compilation.chunks );
const chunk = chunks.find( ( c ) => c.name === pkg );
const hash = chunk && chunk.contentHash && chunk.contentHash.javascript
? chunk.contentHash.javascript
: 'dev';
// Create asset file content
const depsArray = deps.map( ( dep ) => `'${ dep }'` ).join( ', ' );
const assetContent = `<?php return array('dependencies' => array(${ depsArray }), 'version' => '${ hash }');`;
// Determine output filename based on webpack mode
const filename = compilation.options.mode === 'production'
? `${ pkg }/index.min.asset.php`
: `${ pkg }/index.asset.php`;
// Add to compilation assets
compilation.assets[ filename ] = {
source: () => assetContent,
size: () => assetContent.length,
};
} );
callback();
} );
}
}
/**
* Get all @wordpress packages from package.json dependencies.
*
* @return {Array} Array of package names without the @wordpress/ prefix.
*/
function getWordPressPackages() {
// Only use dependencies, not devDependencies (dev packages are build tools)
const deps = packageJson.dependencies || {};
return Object.keys( deps )
.filter( ( packageName ) => packageName.startsWith( WORDPRESS_NAMESPACE ) )
.map( ( packageName ) => packageName.replace( WORDPRESS_NAMESPACE, '' ) );
}
const wordpressPackages = getWordPressPackages();
// If no WordPress packages, return empty config
if ( wordpressPackages.length === 0 ) {
module.exports = {
mode: 'none',
entry: {},
plugins: [
{
apply: ( compiler ) => {
compiler.hooks.done.tap( 'LogNoWordPressPackages', () => {
console.log( '\nℹ No @wordpress packages found in dependencies. Install them with: npm install @wordpress/element' );
} );
},
},
],
};
} else {
// Build entry points for each WordPress package
const entries = {};
wordpressPackages.forEach( ( packageName ) => {
entries[ packageName ] = `@wordpress/${ packageName }`;
} );
module.exports = [
// Production build (minified)
{
mode: 'production',
entry: entries,
output: {
path: join( __dirname, '..', '..', 'build', 'wordpress' ),
filename: '[name]/index.min.js',
library: {
name: [ 'wp', '[name]' ],
type: 'window',
},
},
externals: {
react: 'React',
'react-dom': 'ReactDOM',
},
plugins: [
new WordPressAssetPlugin(),
{
apply: ( compiler ) => {
compiler.hooks.done.tap( 'LogWordPressPackages', () => {
console.log( '\n✓ Built WordPress packages to build/wordpress/:' );
wordpressPackages.forEach( ( pkg ) => {
console.log( ` - @wordpress/${ pkg }` );
} );
console.log( '' );
} );
},
},
],
},
// Development build (for debugging)
{
mode: 'development',
devtool: 'source-map',
entry: entries,
output: {
path: join( __dirname, '..', '..', 'build', 'wordpress' ),
filename: '[name]/index.js',
library: {
name: [ 'wp', '[name]' ],
type: 'window',
},
},
externals: {
react: 'React',
'react-dom': 'ReactDOM',
},
plugins: [
new WordPressAssetPlugin(),
],
},
];
}
