diff --git a/package-lock.json b/package-lock.json index 6f8db2ca574531..e6e8687a475f5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54592,6 +54592,7 @@ "rtlcss-webpack-plugin": "^4.0.7", "sass": "^1.35.2", "sass-loader": "^12.1.0", + "schema-utils": "^4.2.0", "source-map-loader": "^3.0.0", "stylelint": "^14.2.0", "terser-webpack-plugin": "^5.3.9", @@ -54615,15 +54616,16 @@ } }, "packages/scripts/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -54661,6 +54663,49 @@ "webpack": ">=2" } }, + "packages/scripts/node_modules/babel-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "packages/scripts/node_modules/babel-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "packages/scripts/node_modules/babel-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "packages/scripts/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -54747,6 +54792,13 @@ "@sideway/pinpoint": "^2.0.0" } }, + "packages/scripts/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "packages/scripts/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -54841,23 +54893,38 @@ } }, "packages/scripts/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 8.9.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" } }, + "packages/scripts/node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, "packages/scripts/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -68708,6 +68775,7 @@ "rtlcss-webpack-plugin": "^4.0.7", "sass": "^1.35.2", "sass-loader": "^12.1.0", + "schema-utils": "^4.2.0", "source-map-loader": "^3.0.0", "stylelint": "^14.2.0", "terser-webpack-plugin": "^5.3.9", @@ -68719,15 +68787,15 @@ }, "dependencies": { "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" } }, "axios": { @@ -68751,6 +68819,37 @@ "loader-utils": "^2.0.0", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + } } }, "chalk": { @@ -68818,6 +68917,12 @@ "@sideway/pinpoint": "^2.0.0" } }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -68885,14 +68990,26 @@ } }, "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "dependencies": { + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + } } }, "semver": { diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index 4c1d8fa0d1ef32..0155cc24c1051c 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### New Features + +- Update webpack configuration for the `build` and `start` commands to automatically copy PHP files listed in the `variations` field of `block.json` files from the source to the build folder ([#63098](https://github.com/WordPress/gutenberg/pull/63098)). + ## 28.3.0 (2024-07-10) ## 28.2.0 (2024-06-26) diff --git a/packages/scripts/README.md b/packages/scripts/README.md index af892293e00eb2..9973090a3e11e4 100644 --- a/packages/scripts/README.md +++ b/packages/scripts/README.md @@ -97,7 +97,7 @@ This is how you execute the script with presented setup: - `npm run build` - builds the code for production. - `npm run build:custom` - builds the code for production with two entry points and a custom output directory. Paths for custom entry points are relative to the project root. -- `npm run build:copy-php` - builds the code for production and opts into copying all PHP files from the `src` directory and its subfolders to the output directory. By default, only PHP files listed in the `render` field in the detected `block.json` files get copied. +- `npm run build:copy-php` - builds the code for production and opts into copying all PHP files from the `src` directory and its subfolders to the output directory. By default, only PHP files listed in the `render` and `variations` fields in the detected `block.json` files get copied. - `npm run build:custom-directory` - builds the code for production using the `custom-directory` as the source code directory. This script automatically use the optimized config but sometimes you may want to specify some custom options: @@ -382,7 +382,7 @@ This is how you execute the script with presented setup: - `npm start` - starts the build for development. - `npm run start:hot` - starts the build for development with "Fast Refresh". The page will automatically reload if you make changes to the files. - `npm run start:custom` - starts the build for development which contains two entry points and a custom output directory. Paths for custom entry points are relative to the project root. -- `npm run start:copy-php` - starts the build for development and opts into copying all PHP files from the `src` directory and its subfolders to the output directory. By default, only PHP files listed in the `render` field in the detected `block.json` files get copied. +- `npm run start:copy-php` - starts the build for development and opts into copying all PHP files from the `src` directory and its subfolders to the output directory. By default, only PHP files listed in the `render` and `variations` fields in the detected `block.json` files get copied. - `npm run start:custom-directory` - builds the code for production using the `custom-directory` as the source code directory. This script automatically use the optimized config but sometimes you may want to specify some custom options: diff --git a/packages/scripts/config/webpack.config.js b/packages/scripts/config/webpack.config.js index f306fbd54a8a6b..508d4d262942d1 100644 --- a/packages/scripts/config/webpack.config.js +++ b/packages/scripts/config/webpack.config.js @@ -13,6 +13,7 @@ const RtlCssPlugin = require( 'rtlcss-webpack-plugin' ); const TerserPlugin = require( 'terser-webpack-plugin' ); const { realpathSync } = require( 'fs' ); const { sync: glob } = require( 'fast-glob' ); +const { validate } = require( 'schema-utils' ); /** * WordPress dependencies @@ -31,7 +32,7 @@ const { hasPostCSSConfig, getWordPressSrcDirectory, getWebpackEntryPoints, - getRenderPropPaths, + getPhpFilePaths, getAsBooleanFromENV, getBlockJsonModuleFields, getBlockJsonScriptFields, @@ -49,24 +50,45 @@ const hasExperimentalModulesFlag = getAsBooleanFromENV( 'WP_EXPERIMENTAL_MODULES' ); +const phpFilePathsPluginSchema = { + type: 'object', + properties: { + props: { + type: 'array', + items: { + type: 'string', + }, + }, + }, +}; + /** - * The plugin recomputes the render paths once on each compilation. It is necessary to avoid repeating processing + * The plugin recomputes PHP file paths once on each compilation. It is necessary to avoid repeating processing * when filtering every discovered PHP file in the source folder. This is the most performant way to ensure that * changes in `block.json` files are picked up in watch mode. */ -class RenderPathsPlugin { +class PhpFilePathsPlugin { /** - * Paths with the `render` props included in `block.json` files. + * PHP file paths from `render` and `variations` props found in `block.json` files. * * @type {string[]} */ - static renderPaths; + static paths; + + constructor( options = {} ) { + validate( phpFilePathsPluginSchema, options, { + name: 'PHP File Paths Plugin', + baseDataPath: 'options', + } ); + + this.options = options; + } apply( compiler ) { const pluginName = this.constructor.name; compiler.hooks.thisCompilation.tap( pluginName, () => { - this.constructor.renderPaths = getRenderPropPaths(); + this.constructor.paths = getPhpFilePaths( this.options.props ); } ); } } @@ -321,7 +343,7 @@ const scriptConfig = { cleanStaleWebpackAssets: false, } ), - new RenderPathsPlugin(), + new PhpFilePathsPlugin( { props: [ 'render', 'variations' ] } ), new CopyWebpackPlugin( { patterns: [ { @@ -371,7 +393,7 @@ const scriptConfig = { filter: ( filepath ) => { return ( process.env.WP_COPY_PHP_FILES_TO_DIST || - RenderPathsPlugin.renderPaths.includes( + PhpFilePathsPlugin.paths.includes( realpathSync( filepath ).replace( /\\/g, '/' ) ) ); diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 6d735745977daa..6f5218f7978f01 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -82,6 +82,7 @@ "rtlcss-webpack-plugin": "^4.0.7", "sass": "^1.35.2", "sass-loader": "^12.1.0", + "schema-utils": "^4.2.0", "source-map-loader": "^3.0.0", "stylelint": "^14.2.0", "terser-webpack-plugin": "^5.3.9", diff --git a/packages/scripts/utils/config.js b/packages/scripts/utils/config.js index 8b1bbb1ca50590..44bcfb916b1f19 100644 --- a/packages/scripts/utils/config.js +++ b/packages/scripts/utils/config.js @@ -354,6 +354,16 @@ function getWebpackEntryPoints( buildType ) { * @return {Array} The list of all the `render` prop paths included in `block.json` files. */ function getRenderPropPaths() { + return getPhpFilePaths( [ 'render' ] ); +} + +/** + * Returns the list of PHP file paths found in `block.json` files for the given props. + * + * @param {string[]} props The props to search for in the `block.json` files. + * @return {string[]} The list of PHP file paths. + */ +function getPhpFilePaths( props ) { // Continue only if the source directory exists. if ( ! hasProjectFile( getWordPressSrcDirectory() ) ) { return []; @@ -367,20 +377,29 @@ function getRenderPropPaths() { const srcDirectory = fromProjectRoot( getWordPressSrcDirectory() + sep ); - const renderPaths = blockMetadataFiles.map( ( blockMetadataFile ) => { - const { render } = JSON.parse( readFileSync( blockMetadataFile ) ); - if ( render && render.startsWith( 'file:' ) ) { + return blockMetadataFiles.flatMap( ( blockMetadataFile ) => { + const blockJson = JSON.parse( readFileSync( blockMetadataFile ) ); + + const paths = []; + for ( const prop of props ) { + if ( + typeof blockJson?.[ prop ] !== 'string' || + ! blockJson[ prop ]?.startsWith( 'file:' ) + ) { + continue; + } + // Removes the `file:` prefix. const filepath = join( dirname( blockMetadataFile ), - render.replace( 'file:', '' ) + blockJson[ prop ].replace( 'file:', '' ) ); // Takes the path without the file extension, and relative to the defined source directory. if ( ! filepath.startsWith( srcDirectory ) ) { log( chalk.yellow( - `Skipping "${ render.replace( + `Skipping "${ blockJson[ prop ].replace( 'file:', '' ) }" listed in "${ blockMetadataFile.replace( @@ -389,14 +408,12 @@ function getRenderPropPaths() { ) }". File is located outside of the "${ getWordPressSrcDirectory() }" directory.` ) ); - return false; + continue; } - return filepath.replace( /\\/g, '/' ); + paths.push( filepath.replace( /\\/g, '/' ) ); } - return false; + return paths; } ); - - return renderPaths.filter( ( renderPath ) => renderPath ); } module.exports = { @@ -404,6 +421,7 @@ module.exports = { getWebpackArgs, getWordPressSrcDirectory, getWebpackEntryPoints, + getPhpFilePaths, getRenderPropPaths, hasBabelConfig, hasCssnanoConfig, diff --git a/packages/scripts/utils/index.js b/packages/scripts/utils/index.js index 148895ecbc4edf..7c2a3d5ea34254 100644 --- a/packages/scripts/utils/index.js +++ b/packages/scripts/utils/index.js @@ -16,6 +16,7 @@ const { getWebpackArgs, getWordPressSrcDirectory, getWebpackEntryPoints, + getPhpFilePaths, getRenderPropPaths, hasBabelConfig, hasCssnanoConfig, @@ -43,6 +44,7 @@ module.exports = { getWebpackArgs, getWordPressSrcDirectory, getWebpackEntryPoints, + getPhpFilePaths, getRenderPropPaths, getBlockJsonModuleFields, getBlockJsonScriptFields,