-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WP Scripts: Build block.json viewModule #57461
Changes from all commits
0d7235a
e09598e
1689a3a
b519834
6fb6954
1b73c4a
1bf2b9b
cb0e279
6427b50
05f873b
b02b435
7dcc173
15cc7a1
85f2508
040c796
62b6ce3
a5d62a0
afe375d
d59256a
a48053e
cffac91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' ); | ||
const { CleanWebpackPlugin } = require( 'clean-webpack-plugin' ); | ||
const CopyWebpackPlugin = require( 'copy-webpack-plugin' ); | ||
const { DefinePlugin } = require( 'webpack' ); | ||
const webpack = require( 'webpack' ); | ||
const browserslist = require( 'browserslist' ); | ||
const MiniCSSExtractPlugin = require( 'mini-css-extract-plugin' ); | ||
const { basename, dirname, resolve } = require( 'path' ); | ||
|
@@ -30,6 +30,9 @@ const { | |
getWordPressSrcDirectory, | ||
getWebpackEntryPoints, | ||
getRenderPropPaths, | ||
getAsBooleanFromENV, | ||
getBlockJsonModuleFields, | ||
getBlockJsonScriptFields, | ||
} = require( '../utils' ); | ||
|
||
const isProduction = process.env.NODE_ENV === 'production'; | ||
|
@@ -39,6 +42,9 @@ if ( ! browserslist.findConfig( '.' ) ) { | |
target += ':' + fromConfigRoot( '.browserslistrc' ); | ||
} | ||
const hasReactFastRefresh = hasArgInCLI( '--hot' ) && ! isProduction; | ||
const hasExperimentalModulesFlag = getAsBooleanFromENV( | ||
'WP_EXPERIMENTAL_MODULES' | ||
); | ||
|
||
/** | ||
* The plugin recomputes the render paths once on each compilation. It is necessary to avoid repeating processing | ||
|
@@ -110,10 +116,10 @@ const cssLoaders = [ | |
}, | ||
]; | ||
|
||
const config = { | ||
/** @type {webpack.Configuration} */ | ||
const baseConfig = { | ||
mode, | ||
target, | ||
entry: getWebpackEntryPoints, | ||
output: { | ||
filename: '[name].js', | ||
path: resolve( process.cwd(), 'build' ), | ||
|
@@ -165,7 +171,7 @@ const config = { | |
module: { | ||
rules: [ | ||
{ | ||
test: /\.(j|t)sx?$/, | ||
test: /\.m?(j|t)sx?$/, | ||
exclude: /node_modules/, | ||
use: [ | ||
{ | ||
|
@@ -245,21 +251,72 @@ const config = { | |
}, | ||
], | ||
}, | ||
stats: { | ||
children: false, | ||
}, | ||
}; | ||
|
||
// WP_DEVTOOL global variable controls how source maps are generated. | ||
// See: https://webpack.js.org/configuration/devtool/#devtool. | ||
if ( process.env.WP_DEVTOOL ) { | ||
baseConfig.devtool = process.env.WP_DEVTOOL; | ||
} | ||
|
||
if ( ! isProduction ) { | ||
// Set default sourcemap mode if it wasn't set by WP_DEVTOOL. | ||
baseConfig.devtool = baseConfig.devtool || 'source-map'; | ||
} | ||
|
||
// Add source-map-loader if devtool is set, whether in dev mode or not. | ||
if ( baseConfig.devtool ) { | ||
baseConfig.module.rules.unshift( { | ||
test: /\.(j|t)sx?$/, | ||
exclude: [ /node_modules/ ], | ||
use: require.resolve( 'source-map-loader' ), | ||
enforce: 'pre', | ||
} ); | ||
} | ||
|
||
/** @type {webpack.Configuration} */ | ||
const scriptConfig = { | ||
...baseConfig, | ||
|
||
entry: getWebpackEntryPoints( 'script' ), | ||
|
||
devServer: isProduction | ||
? undefined | ||
: { | ||
devMiddleware: { | ||
writeToDisk: true, | ||
}, | ||
allowedHosts: 'auto', | ||
host: 'localhost', | ||
port: 8887, | ||
proxy: { | ||
'/build': { | ||
pathRewrite: { | ||
'^/build': '', | ||
}, | ||
}, | ||
}, | ||
}, | ||
|
||
plugins: [ | ||
new DefinePlugin( { | ||
new webpack.DefinePlugin( { | ||
// Inject the `SCRIPT_DEBUG` global, used for development features flagging. | ||
SCRIPT_DEBUG: ! isProduction, | ||
} ), | ||
// During rebuilds, all webpack assets that are not used anymore will be | ||
// removed automatically. There is an exception added in watch mode for | ||
// fonts and images. It is a known limitations: | ||
// https://github.com/johnagan/clean-webpack-plugin/issues/159 | ||
new CleanWebpackPlugin( { | ||
cleanAfterEveryBuildPatterns: [ '!fonts/**', '!images/**' ], | ||
// Prevent it from deleting webpack assets during builds that have | ||
// multiple configurations returned in the webpack config. | ||
cleanStaleWebpackAssets: false, | ||
} ), | ||
|
||
// If we run a modules build, the 2 compilations can "clean" each other's output | ||
// Prevent the cleaning from happening | ||
! hasExperimentalModulesFlag && | ||
new CleanWebpackPlugin( { | ||
cleanAfterEveryBuildPatterns: [ '!fonts/**', '!images/**' ], | ||
// Prevent it from deleting webpack assets during builds that have | ||
// multiple configurations returned in the webpack config. | ||
cleanStaleWebpackAssets: false, | ||
} ), | ||
|
||
new RenderPathsPlugin(), | ||
new CopyWebpackPlugin( { | ||
patterns: [ | ||
|
@@ -269,27 +326,33 @@ const config = { | |
noErrorOnMissing: true, | ||
transform( content, absoluteFrom ) { | ||
const convertExtension = ( path ) => { | ||
return path.replace( /\.(j|t)sx?$/, '.js' ); | ||
return path.replace( /\.m?(j|t)sx?$/, '.js' ); | ||
}; | ||
|
||
if ( basename( absoluteFrom ) === 'block.json' ) { | ||
const blockJson = JSON.parse( content.toString() ); | ||
[ 'viewScript', 'script', 'editorScript' ].forEach( | ||
( key ) => { | ||
if ( Array.isArray( blockJson[ key ] ) ) { | ||
blockJson[ key ] = | ||
blockJson[ key ].map( | ||
convertExtension | ||
); | ||
} else if ( | ||
typeof blockJson[ key ] === 'string' | ||
) { | ||
blockJson[ key ] = convertExtension( | ||
blockJson[ key ] | ||
); | ||
|
||
[ | ||
getBlockJsonScriptFields( blockJson ), | ||
getBlockJsonModuleFields( blockJson ), | ||
].forEach( ( fields ) => { | ||
if ( fields ) { | ||
for ( const [ | ||
key, | ||
value, | ||
] of Object.entries( fields ) ) { | ||
if ( Array.isArray( value ) ) { | ||
blockJson[ key ] = | ||
value.map( convertExtension ); | ||
} else if ( | ||
typeof value === 'string' | ||
) { | ||
blockJson[ key ] = | ||
convertExtension( value ); | ||
} | ||
} | ||
} | ||
); | ||
} ); | ||
|
||
return JSON.stringify( blockJson, null, 2 ); | ||
} | ||
|
@@ -317,52 +380,55 @@ const config = { | |
process.env.WP_BUNDLE_ANALYZER && new BundleAnalyzerPlugin(), | ||
// MiniCSSExtractPlugin to extract the CSS thats gets imported into JavaScript. | ||
new MiniCSSExtractPlugin( { filename: '[name].css' } ), | ||
// React Fast Refresh. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like React Fast Refresh was misplaced. It should remain here for the scripts configuration as it works withe the existing Babel config and the dev server. It won’t work with the modules config as it’s missing proper Babel handling, the dev server integration. In addition, I’m not sure whether it’s even possible to make it work with Preact. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're absolutely correct, thanks: #57777 |
||
hasReactFastRefresh && new ReactRefreshWebpackPlugin(), | ||
// WP_NO_EXTERNALS global variable controls whether scripts' assets get | ||
// generated, and the default externals set. | ||
! process.env.WP_NO_EXTERNALS && | ||
new DependencyExtractionWebpackPlugin(), | ||
].filter( Boolean ), | ||
stats: { | ||
children: false, | ||
}, | ||
}; | ||
|
||
// WP_DEVTOOL global variable controls how source maps are generated. | ||
// See: https://webpack.js.org/configuration/devtool/#devtool. | ||
if ( process.env.WP_DEVTOOL ) { | ||
config.devtool = process.env.WP_DEVTOOL; | ||
} | ||
if ( hasExperimentalModulesFlag ) { | ||
/** @type {webpack.Configuration} */ | ||
const moduleConfig = { | ||
...baseConfig, | ||
|
||
if ( ! isProduction ) { | ||
// Set default sourcemap mode if it wasn't set by WP_DEVTOOL. | ||
config.devtool = config.devtool || 'source-map'; | ||
config.devServer = { | ||
devMiddleware: { | ||
writeToDisk: true, | ||
entry: getWebpackEntryPoints( 'module' ), | ||
|
||
experiments: { | ||
...baseConfig.experiments, | ||
outputModule: true, | ||
}, | ||
allowedHosts: 'auto', | ||
host: 'localhost', | ||
port: 8887, | ||
proxy: { | ||
'/build': { | ||
pathRewrite: { | ||
'^/build': '', | ||
}, | ||
|
||
output: { | ||
...baseConfig.output, | ||
module: true, | ||
chunkFormat: 'module', | ||
library: { | ||
...baseConfig.output.library, | ||
type: 'module', | ||
}, | ||
}, | ||
|
||
plugins: [ | ||
new webpack.DefinePlugin( { | ||
// Inject the `SCRIPT_DEBUG` global, used for development features flagging. | ||
SCRIPT_DEBUG: ! isProduction, | ||
} ), | ||
// The WP_BUNDLE_ANALYZER global variable enables a utility that represents | ||
// bundle content as a convenient interactive zoomable treemap. | ||
process.env.WP_BUNDLE_ANALYZER && new BundleAnalyzerPlugin(), | ||
// MiniCSSExtractPlugin to extract the CSS thats gets imported into JavaScript. | ||
new MiniCSSExtractPlugin( { filename: '[name].css' } ), | ||
// React Fast Refresh. | ||
hasReactFastRefresh && new ReactRefreshWebpackPlugin(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don’t think React Fast Refresh will work out of the box for viewModule. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it may work, but I'm not really sure how to test. Interactivity uses preact, which I don't think is compatible with the react refresh work. I saw this issue about refresh support for preact preactjs/preact#2184 We probably just remove this for now for modules. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
// WP_NO_EXTERNALS global variable controls whether scripts' assets get | ||
// generated, and the default externals set. | ||
! process.env.WP_NO_EXTERNALS && | ||
new DependencyExtractionWebpackPlugin(), | ||
].filter( Boolean ), | ||
}; | ||
} | ||
|
||
// Add source-map-loader if devtool is set, whether in dev mode or not. | ||
if ( config.devtool ) { | ||
config.module.rules.unshift( { | ||
test: /\.(j|t)sx?$/, | ||
exclude: [ /node_modules/ ], | ||
use: require.resolve( 'source-map-loader' ), | ||
enforce: 'pre', | ||
} ); | ||
module.exports = [ scriptConfig, moduleConfig ]; | ||
} else { | ||
module.exports = scriptConfig; | ||
} | ||
|
||
module.exports = config; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
const moduleFields = new Set( [ 'viewModule' ] ); | ||
const scriptFields = new Set( [ 'viewScript', 'script', 'editorScript' ] ); | ||
|
||
/** | ||
* @param {Object} blockJson | ||
* @return {null|Record<string, unknown>} Fields | ||
*/ | ||
function getBlockJsonModuleFields( blockJson ) { | ||
let result = null; | ||
for ( const field of moduleFields ) { | ||
if ( Object.hasOwn( blockJson, field ) ) { | ||
if ( ! result ) { | ||
result = {}; | ||
} | ||
result[ field ] = blockJson[ field ]; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* @param {Object} blockJson | ||
* @return {null|Record<string, unknown>} Fields | ||
*/ | ||
function getBlockJsonScriptFields( blockJson ) { | ||
let result = null; | ||
for ( const field of scriptFields ) { | ||
if ( Object.hasOwn( blockJson, field ) ) { | ||
if ( ! result ) { | ||
result = {}; | ||
} | ||
result[ field ] = blockJson[ field ]; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
module.exports = { | ||
getBlockJsonModuleFields, | ||
getBlockJsonScriptFields, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be refactored back to a callback function. There are two reasons:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a callback, the signature changed so this now returns the callback:
gutenberg/packages/scripts/utils/config.js
Lines 188 to 202 in 05b6bd7
I'm investigating the problem now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean if I change block.json (to add another file) when
wp-scripts start --experimental-modules
is running, the build doesn't pick it up?I don't think this has changed at all for script builds or for the script compilation of module builds. I believe the copy webpack plugin is adding the block.json files to the build which makes webpack observe those changes.
Because the modules compilation doesn't include the copy plugins for block.json, when block.json changes the module compilation won't pick it up. I'll look into how to make it observe those changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like it should work correctly for scripts when the is no experimental flag provided 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a PR to add
block.json
files as webpack file dependencies so they're watched from the module build: #57927