Wordpress/tools/webpack/packages.js
Jorge Costa ac4de42983 Scripts: Use core-js url as polyfill for window.URL.
URL polyfill from the polyfill-library library is not spec-conformant, in a way which negatively impacts its usability for pending revisions to the block editor. Specifically, there were revisions to the implementation of the wp-url script to detect URL validity by relying on thrown errors from the URL constructor, but this specific behavior is not implemented in this version of the polyfill.
There was another issue in r47238, which is that the test used to check whether the polyfill should be included is not accurate. This commit uses a different check and fixes the issue.

Props aduth.
Fixes: #49360.

git-svn-id: https://develop.svn.wordpress.org/trunk@47416 602fd350-edb4-49c9-b593-d223f7449a82
2020-03-03 16:53:32 +00:00

285 lines
8.2 KiB
JavaScript

/**
* External dependencies
*/
const { DefinePlugin } = require( 'webpack' );
const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
const LiveReloadPlugin = require( 'webpack-livereload-plugin' );
const postcss = require( 'postcss' );
const UglifyJS = require( 'uglify-js' );
const { join, basename } = require( 'path' );
const { get } = require( 'lodash' );
/**
* WordPress dependencies
*/
const CustomTemplatedPathPlugin = require( '@wordpress/custom-templated-path-webpack-plugin' );
const DependencyExtractionPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );
const LibraryExportDefaultPlugin = require( '@wordpress/library-export-default-webpack-plugin' );
/**
* Internal dependencies
*/
const { dependencies } = require( '../../package' );
const baseDir = join( __dirname, '../../' );
/**
* Given a string, returns a new string with dash separators converedd to
* camel-case equivalent. This is not as aggressive as `_.camelCase` in
* converting to uppercase, where Lodash will convert letters following
* numbers.
*
* @param {string} string Input dash-delimited string.
*
* @return {string} Camel-cased string.
*/
function camelCaseDash( string ) {
return string.replace(
/-([a-z])/g,
( match, letter ) => letter.toUpperCase()
);
}
/**
* Maps vendors to copy commands for the CopyWebpackPlugin.
*
* @param {Object} vendors Vendors to include in the vendor folder.
* @param {string} buildTarget The folder in which to build the packages.
*
* @return {Object[]} Copy object suitable for the CopyWebpackPlugin.
*/
function mapVendorCopies( vendors, buildTarget ) {
return Object.keys( vendors ).map( ( filename ) => ( {
from: join( baseDir, `node_modules/${ vendors[ filename ] }` ),
to: join( baseDir, `${ buildTarget }/js/dist/vendor/${ filename }` ),
} ) );
}
module.exports = function( env = { environment: 'production', watch: false, buildTarget: false } ) {
const mode = env.environment;
const suffix = mode === 'production' ? '.min' : '';
let buildTarget = env.buildTarget ? env.buildTarget : ( mode === 'production' ? 'build' : 'src' );
buildTarget = buildTarget + '/wp-includes';
const WORDPRESS_NAMESPACE = '@wordpress/';
const BUNDLED_PACKAGES = [ '@wordpress/icons' ];
const packages = Object.keys( dependencies )
.filter( ( packageName ) =>
! BUNDLED_PACKAGES.includes( packageName ) &&
packageName.startsWith( WORDPRESS_NAMESPACE )
)
.map( ( packageName ) => packageName.replace( WORDPRESS_NAMESPACE, '' ) );
const vendors = {
'lodash.js': 'lodash/lodash.js',
'wp-polyfill.js': '@babel/polyfill/dist/polyfill.js',
'wp-polyfill-fetch.js': 'whatwg-fetch/dist/fetch.umd.js',
'wp-polyfill-element-closest.js': 'element-closest/element-closest.js',
'wp-polyfill-node-contains.js': 'polyfill-library/polyfills/Node/prototype/contains/polyfill.js',
'wp-polyfill-url.js': 'core-js-url-browser/url.js',
'wp-polyfill-dom-rect.js': 'polyfill-library/polyfills/DOMRect/polyfill.js',
'wp-polyfill-formdata.js': 'formdata-polyfill/FormData.js',
'moment.js': 'moment/moment.js',
'react.js': 'react/umd/react.development.js',
'react-dom.js': 'react-dom/umd/react-dom.development.js',
};
const minifiedVendors = {
'lodash.min.js': 'lodash/lodash.min.js',
'wp-polyfill.min.js': '@babel/polyfill/dist/polyfill.min.js',
'wp-polyfill-formdata.min.js': 'formdata-polyfill/formdata.min.js',
'wp-polyfill-url.min.js': 'core-js-url-browser/url.min.js',
'moment.min.js': 'moment/min/moment.min.js',
'react.min.js': 'react/umd/react.production.min.js',
'react-dom.min.js': 'react-dom/umd/react-dom.production.min.js',
};
const minifyVendors = {
'wp-polyfill-fetch.min.js': 'whatwg-fetch/dist/fetch.umd.js',
'wp-polyfill-element-closest.min.js': 'element-closest/element-closest.js',
'wp-polyfill-node-contains.min.js': 'polyfill-library/polyfills/Node/prototype/contains/polyfill.js',
'wp-polyfill-dom-rect.min.js': 'polyfill-library/polyfills/DOMRect/polyfill.js',
};
const blockNames = [
'archives',
'block',
'calendar',
'categories',
'latest-comments',
'latest-posts',
'rss',
'search',
'shortcode',
'social-link',
'tag-cloud',
];
const phpFiles = {
'block-serialization-default-parser/parser.php': 'wp-includes/class-wp-block-parser.php',
...blockNames.reduce( ( files, blockName ) => {
files[ `block-library/src/${ blockName }/index.php` ] = `wp-includes/blocks/${ blockName }.php`;
return files;
} , {} ),
};
const blockMetadataCopies = {
from: join( baseDir, `node_modules/@wordpress/block-library/src/+(${ blockNames.join( '|' ) })/block.json` ),
test: new RegExp( `\/([^/]+)\/block\.json$` ),
to: join( baseDir, `${ buildTarget }/blocks/[1]/block.json` ),
};
const developmentCopies = mapVendorCopies( vendors, buildTarget );
const minifiedCopies = mapVendorCopies( minifiedVendors, buildTarget );
const minifyCopies = mapVendorCopies( minifyVendors, buildTarget ).map( ( copyCommand ) => {
return {
...copyCommand,
transform: ( content ) => {
return UglifyJS.minify( content.toString() ).code;
},
};
} );
let vendorCopies = mode === "development" ? developmentCopies : [ ...minifiedCopies, ...minifyCopies ];
let cssCopies = packages.map( ( packageName ) => ( {
from: join( baseDir, `node_modules/@wordpress/${ packageName }/build-style/*.css` ),
to: join( baseDir, `${ buildTarget }/css/dist/${ packageName }/` ),
flatten: true,
transform: ( content ) => {
if ( mode === 'production' ) {
return postcss( [
require( 'cssnano' )( {
preset: 'default',
} ),
] )
.process( content, { from: 'src/app.css', to: 'dest/app.css' } )
.then( ( result ) => result.css );
}
return content;
},
transformPath: ( targetPath, sourcePath ) => {
if ( mode === 'production' ) {
return targetPath.replace( /\.css$/, '.min.css' );
}
return targetPath;
}
} ) );
const phpCopies = Object.keys( phpFiles ).map( ( filename ) => ( {
from: join( baseDir, `node_modules/@wordpress/${ filename }` ),
to: join( baseDir, `src/${ phpFiles[ filename ] }` ),
} ) );
const config = {
mode,
entry: packages.reduce( ( memo, packageName ) => {
const name = camelCaseDash( packageName );
memo[ name ] = join( baseDir, `node_modules/@wordpress/${ packageName }` );
return memo;
}, {} ),
output: {
devtoolNamespace: 'wp',
filename: `[basename]${ suffix }.js`,
path: join( baseDir, `${ buildTarget }/js/dist` ),
library: {
root: [ 'wp', '[name]' ]
},
libraryTarget: 'this',
},
resolve: {
modules: [
baseDir,
'node_modules',
],
alias: {
'lodash-es': 'lodash',
},
},
module: {
rules: [
{
test: /\.js$/,
use: [ 'source-map-loader' ],
enforce: 'pre',
},
],
},
plugins: [
new DefinePlugin( {
// Inject the `GUTENBERG_PHASE` global, used for feature flagging.
'process.env.GUTENBERG_PHASE': 1,
} ),
new LibraryExportDefaultPlugin( [
'api-fetch',
'deprecated',
'dom-ready',
'redux-routine',
'token-list',
'server-side-render',
'shortcode',
'warning',
].map( camelCaseDash ) ),
new CustomTemplatedPathPlugin( {
basename( path, data ) {
let rawRequest;
const entryModule = get( data, [ 'chunk', 'entryModule' ], {} );
switch ( entryModule.type ) {
case 'javascript/auto':
rawRequest = entryModule.rawRequest;
break;
case 'javascript/esm':
rawRequest = entryModule.rootModule.rawRequest;
break;
}
if ( rawRequest ) {
return basename( rawRequest );
}
return path;
},
} ),
new DependencyExtractionPlugin( {
injectPolyfill: true,
combineAssets: true,
} ),
new CopyWebpackPlugin(
[
...vendorCopies,
...cssCopies,
...phpCopies,
blockMetadataCopies,
],
),
],
stats: {
children: false,
},
watch: env.watch,
};
if ( config.mode !== 'production' ) {
config.devtool = process.env.SOURCEMAP || 'source-map';
}
if ( mode === 'development' && env.buildTarget === 'build/' ) {
delete config.devtool;
config.mode = 'production';
config.optimization = {
minimize: false
};
}
if ( config.mode === 'development' ) {
config.plugins.push( new LiveReloadPlugin( { port: process.env.WORDPRESS_LIVE_RELOAD_PORT || 35729 } ) );
}
return config;
};