Build tools: Build @wordpress packages with webpack.

We decided to split the media webpack config into it's own file. The
main webpack config then combines this file with the packages config.

Include vendor scripts by copying them. We copy the minified files if
they are available. If they aren't available we minify the original
files ourselves.

Props omarreiss, herregroen, gziolo, youknowriad, netweb, adamsilverstein.
Merges [43719] to trunk.
See #45065.


git-svn-id: https://develop.svn.wordpress.org/trunk@44112 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Anton Timmermans 2018-12-13 15:25:37 +00:00
parent f284024e6b
commit 7a2fa10e28
6 changed files with 4684 additions and 245 deletions

View File

@ -704,7 +704,8 @@ module.exports = function(grunt) {
},
webpack: {
prod: webpackConfig( { environment: 'production' } ),
dev: webpackConfig( { environment: 'development' } )
dev: webpackConfig( { environment: 'development' } ),
watch: webpackConfig( { environment: 'development', watch: true } )
},
concat: {
tinymce: {

4525
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,8 +13,12 @@
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
"devDependencies": {
"@wordpress/custom-templated-path-webpack-plugin": "^1.1.5",
"@wordpress/library-export-default-webpack-plugin": "^1.0.4",
"autoprefixer": "^9.1.5",
"check-node-version": "3.2.0",
"copy-webpack-plugin": "^4.5.2",
"cssnano": "^4.1.4",
"grunt": "~1.0.3",
"grunt-banner": "^0.6.0",
"grunt-contrib-clean": "~2.0.0",
@ -41,20 +45,66 @@
"ink-docstrap": "^1.3.0",
"jquery-migrate": "1.4.1",
"matchdep": "~2.0.0",
"source-map-loader": "^0.2.4",
"uglify-js": "^3.4.9",
"uglifyjs-webpack-plugin": "^2.0.1",
"webpack": "^4.20.2",
"webpack-dev-server": "^3.1.9"
"webpack-dev-server": "^3.1.9",
"webpack-livereload-plugin": "^2.1.1"
},
"dependencies": {
"@babel/polyfill": "^7.0.0",
"@wordpress/a11y": "^2.0.2",
"@wordpress/api-fetch": "^2.0.2",
"@wordpress/autop": "^2.0.2",
"@wordpress/blob": "^2.0.2",
"@wordpress/block-library": "2.1.0",
"@wordpress/block-serialization-default-parser": "^1.0.1",
"@wordpress/blocks": "^4.0.1",
"@wordpress/components": "^4.1.0",
"@wordpress/compose": "^2.0.2",
"@wordpress/core-data": "^2.0.2",
"@wordpress/data": "^2.1.1",
"@wordpress/date": "^2.0.2",
"@wordpress/deprecated": "^2.0.2",
"@wordpress/dom": "^2.0.2",
"@wordpress/dom-ready": "^2.0.2",
"@wordpress/edit-post": "1.0.0",
"@wordpress/editor": "^4.0.1",
"@wordpress/element": "^2.1.1",
"@wordpress/escape-html": "^1.0.0-beta.1",
"@wordpress/hooks": "^2.0.2",
"@wordpress/html-entities": "^2.0.2",
"@wordpress/i18n": "^3.0.1",
"@wordpress/is-shallow-equal": "^1.1.4",
"@wordpress/keycodes": "^2.0.2",
"@wordpress/list-reusable-blocks": "^1.1.0",
"@wordpress/nux": "^2.0.1",
"@wordpress/plugins": "^2.0.2",
"@wordpress/redux-routine": "^3.0.1",
"@wordpress/rich-text": "^1.0.0-beta.1",
"@wordpress/shortcode": "^2.0.2",
"@wordpress/token-list": "^1.0.2",
"@wordpress/url": "^2.0.2",
"@wordpress/viewport": "^2.0.2",
"@wordpress/wordcount": "^2.0.2",
"backbone": "1.3.3",
"element-closest": "^2.0.2",
"formdata-polyfill": "^3.0.12",
"imagesloaded": "3.2.0",
"jquery": "1.12.4",
"jquery-color": "github:jquery/jquery-color#2.1.1",
"jquery-form": "4.2.1",
"jquery-hoverintent": "1.8.3",
"jquery-ui": "github:jquery/jquery-ui#1.11.4",
"lodash": "^4.17.11",
"masonry-layout": "3.3.2",
"moment": "^2.22.2",
"polyfill-library": "^3.26.0-0",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"twemoji": "11.0.0",
"underscore": "1.8.3"
"underscore": "1.8.3",
"whatwg-fetch": "^3.0.0"
}
}

43
tools/webpack/media.js Normal file
View File

@ -0,0 +1,43 @@
const UglifyJsPlugin = require( 'uglifyjs-webpack-plugin' );
var path = require( 'path' ),
admin_files = {},
include_files = {};
include_files = {
'build/wp-includes/js/media-audiovideo.js': ['./src/js/_enqueues/wp/media/audiovideo.js'],
'build/wp-includes/js/media-audiovideo.min.js': ['./src/js/_enqueues/wp/media/audiovideo.js'],
'build/wp-includes/js/media-grid.js': ['./src/js/_enqueues/wp/media/grid.js'],
'build/wp-includes/js/media-grid.min.js': ['./src/js/_enqueues/wp/media/grid.js'],
'build/wp-includes/js/media-models.js': ['./src/js/_enqueues/wp/media/models.js'],
'build/wp-includes/js/media-models.min.js': ['./src/js/_enqueues/wp/media/models.js'],
'build/wp-includes/js/media-views.js': ['./src/js/_enqueues/wp/media/views.js'],
'build/wp-includes/js/media-views.min.js': ['./src/js/_enqueues/wp/media/views.js'],
};
const baseDir = path.join( __dirname, '../../' );
module.exports = function( env = { environment: 'production', watch: false } ) {
const mode = env.environment;
const mediaConfig = {
mode,
cache: true,
entry: Object.assign( admin_files, include_files ),
output: {
path: baseDir,
filename: '[name]',
},
optimization: {
minimize: true,
minimizer: [
new UglifyJsPlugin( {
include: /\.min\.js$/,
} )
]
},
watch: env.watch,
};
return mediaConfig;
};

254
tools/webpack/packages.js Normal file
View File

@ -0,0 +1,254 @@
/**
* External dependencies
*/
const LiveReloadPlugin = require( 'webpack-livereload-plugin' );
const CopyWebpackPlugin = require( 'copy-webpack-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 LibraryExportDefaultPlugin = require( '@wordpress/library-export-default-webpack-plugin' );
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.
*
* @return {Object[]} Copy object suitable for the CopyWebpackPlugin.
*/
function mapVendorCopies( vendors ) {
return Object.keys( vendors ).map( ( filename ) => ( {
from: join( baseDir, `node_modules/${ vendors[ filename ] }` ),
to: join( baseDir, `build/js/dist/vendor/${ filename }` ),
} ) );
}
module.exports = function( env = { environment: 'production', watch: false } ) {
const mode = env.environment;
const suffix = mode === 'production' ? '.min': '';
const packages = [
'api-fetch',
'a11y',
'autop',
'blob',
'blocks',
'block-library',
'block-serialization-default-parser',
'components',
'compose',
'core-data',
'data',
'date',
'deprecated',
'dom',
'dom-ready',
'edit-post',
'editor',
'element',
'escape-html',
'hooks',
'html-entities',
'i18n',
'is-shallow-equal',
'keycodes',
'list-reusable-blocks',
'nux',
'plugins',
'redux-routine',
'rich-text',
'shortcode',
'token-list',
'url',
'viewport',
'wordcount',
];
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-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',
'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',
};
const externals = {
react: 'React',
'react-dom': 'ReactDOM',
tinymce: 'tinymce',
moment: 'moment',
jquery: 'jQuery',
lodash: 'lodash',
'lodash-es': 'lodash',
};
packages.forEach( ( name ) => {
externals[ `@wordpress/${ name }` ] = {
this: [ 'wp', camelCaseDash( name ) ],
};
} );
const developmentCopies = mapVendorCopies( vendors );
const minifiedCopies = mapVendorCopies( minifiedVendors );
const minifyCopies = mapVendorCopies( minifyVendors ).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, `build/styles/dist/${ packageName }/` ),
flatten: true,
transform: ( content ) => {
if ( config.mode === 'production' ) {
return postcss( [
require( 'cssnano' )( {
preset: 'default',
} ),
] )
.process( content, { from: 'src/app.css', to: 'dest/app.css' } )
.then( ( result ) => result.css );
}
return content;
}
} ) );
const config = {
mode,
entry: packages.reduce( ( memo, packageName ) => {
const name = camelCaseDash( packageName );
memo[ name ] = join( baseDir, `node_modules/@wordpress/${ packageName }` );
return memo;
}, {} ),
output: {
filename: `[basename]${ suffix }.js`,
path: join( baseDir, 'build/js/dist' ),
library: {
root: [ 'wp', '[name]' ]
},
libraryTarget: 'this',
},
externals,
resolve: {
modules: [
baseDir,
'node_modules',
],
alias: {
'lodash-es': 'lodash',
},
},
module: {
rules: [
{
test: /\.js$/,
use: [ 'source-map-loader' ],
enforce: 'pre',
},
],
},
plugins: [
new LibraryExportDefaultPlugin( [
'api-fetch',
'deprecated',
'dom-ready',
'redux-routine',
].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 CopyWebpackPlugin(
[
...vendorCopies,
...cssCopies,
],
),
],
stats: {
children: false,
},
watch: env.watch,
};
if ( config.mode !== 'production' ) {
config.devtool = process.env.SOURCEMAP || 'source-map';
}
if ( config.mode === 'development' ) {
config.plugins.push( new LiveReloadPlugin( { port: process.env.WORDPRESS_LIVE_RELOAD_PORT || 35729 } ) );
}
return config;
};

View File

@ -1,41 +1,15 @@
const UglifyJsPlugin = require( 'uglifyjs-webpack-plugin' );
const mediaConfig = require( './tools/webpack/media' );
const packagesConfig = require( './tools/webpack/packages' );
var path = require( 'path' ),
webpack = require( 'webpack' ),
admin_files = {},
include_files = {};
module.exports = function( env = { environment: "production", watch: false } ) {
if ( ! env.watch ) {
env.watch = false;
}
include_files = {
'build/wp-includes/js/media-audiovideo.js': ['./src/js/_enqueues/wp/media/audiovideo.js'],
'build/wp-includes/js/media-audiovideo.min.js': ['./src/js/_enqueues/wp/media/audiovideo.js'],
'build/wp-includes/js/media-grid.js': ['./src/js/_enqueues/wp/media/grid.js'],
'build/wp-includes/js/media-grid.min.js': ['./src/js/_enqueues/wp/media/grid.js'],
'build/wp-includes/js/media-models.js': ['./src/js/_enqueues/wp/media/models.js'],
'build/wp-includes/js/media-models.min.js': ['./src/js/_enqueues/wp/media/models.js'],
'build/wp-includes/js/media-views.js': ['./src/js/_enqueues/wp/media/views.js'],
'build/wp-includes/js/media-views.min.js': ['./src/js/_enqueues/wp/media/views.js'],
};
module.exports = function( env = { environment: "production" } ) {
const mode = env.environment;
const mediaConfig = {
mode,
cache: true,
entry: Object.assign( admin_files, include_files ),
output: {
path: path.resolve( __dirname ),
filename: '[name]',
},
optimization: {
minimize: true,
minimizer: [
new UglifyJsPlugin( {
include: /\.min\.js$/,
} )
]
},
};
return mediaConfig;
const config = [
mediaConfig( env ),
packagesConfig( env ),
];
return config;
};