diff --git a/assets/src/blocks/example-block/block.json b/assets/src/blocks/example-block/block.json
new file mode 100644
index 0000000..7ecff80
--- /dev/null
+++ b/assets/src/blocks/example-block/block.json
@@ -0,0 +1,9 @@
+{
+ "apiVersion": 3,
+ "name": "elementary/example-block",
+ "title": "Example Block",
+ "category": "widgets",
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css",
+ "style": "file:./style-index.css"
+}
diff --git a/assets/src/blocks/example-block/edit.js b/assets/src/blocks/example-block/edit.js
new file mode 100644
index 0000000..f525e4e
--- /dev/null
+++ b/assets/src/blocks/example-block/edit.js
@@ -0,0 +1,5 @@
+import { useBlockProps } from '@wordpress/block-editor';
+
+export default function Edit() {
+ return
Example Block
;
+}
diff --git a/assets/src/blocks/example-block/editor.scss b/assets/src/blocks/example-block/editor.scss
new file mode 100644
index 0000000..6df36bc
--- /dev/null
+++ b/assets/src/blocks/example-block/editor.scss
@@ -0,0 +1,5 @@
+.wp-block-elementary-example-block {
+ padding: 16px;
+ border: 1px dashed #ccc;
+ background: #f7f7f7;
+}
diff --git a/assets/src/blocks/example-block/index.js b/assets/src/blocks/example-block/index.js
new file mode 100644
index 0000000..fda8258
--- /dev/null
+++ b/assets/src/blocks/example-block/index.js
@@ -0,0 +1,12 @@
+import { registerBlockType } from '@wordpress/blocks';
+import Edit from './edit';
+import save from './save';
+import metadata from './block.json';
+
+import './editor.scss';
+import './style.scss';
+
+registerBlockType(metadata.name, {
+ edit: Edit,
+ save,
+});
diff --git a/assets/src/blocks/example-block/save.js b/assets/src/blocks/example-block/save.js
new file mode 100644
index 0000000..c2abb06
--- /dev/null
+++ b/assets/src/blocks/example-block/save.js
@@ -0,0 +1,5 @@
+import { useBlockProps } from '@wordpress/block-editor';
+
+export default function save() {
+ return Example Block
;
+}
diff --git a/assets/src/blocks/example-block/style.scss b/assets/src/blocks/example-block/style.scss
new file mode 100644
index 0000000..01e6b12
--- /dev/null
+++ b/assets/src/blocks/example-block/style.scss
@@ -0,0 +1,4 @@
+.wp-block-elementary-example-block {
+ padding: 16px;
+ border: 1px solid #ddd;
+}
diff --git a/inc/classes/class-assets.php b/inc/classes/class-assets.php
index 18bca00..9dfd85a 100644
--- a/inc/classes/class-assets.php
+++ b/inc/classes/class-assets.php
@@ -32,6 +32,7 @@ protected function __construct() {
* @since 1.0.0
*/
public function setup_hooks() {
+ add_action( 'init', [ $this, 'register_blocks' ] );
add_action( 'wp_enqueue_scripts', [ $this, 'register_assets' ] );
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );
add_filter( 'render_block', [ $this, 'enqueue_block_specific_assets' ], 10, 2 );
@@ -169,4 +170,31 @@ public function get_file_version( $file, $ver = false ) {
public function enqueue_assets() {
wp_enqueue_style( 'elementary-theme-styles' );
}
+
+ /**
+ * Register theme blocks.
+ *
+ * @since 1.0.0
+ *
+ * @return void
+ */
+ public function register_blocks() {
+
+ $blocks_dir = ELEMENTARY_THEME_TEMP_DIR . '/assets/build/blocks';
+
+ // print_r( $blocks_dir );
+ // exit;
+
+ if ( ! is_dir( $blocks_dir ) ) {
+ return;
+ }
+
+ // List all subdirectories in 'inc/blocks' directory.
+ $blocks = array_filter( glob( $blocks_dir . '/*' ), 'is_dir' );
+
+ // Register each block.
+ foreach ( $blocks as $block ) {
+ register_block_type( $block );
+ }
+ }
}
diff --git a/package-lock.json b/package-lock.json
index 0bc763d..9e27bd5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -30,7 +30,8 @@
"jest-silent-reporter": "0.6.0",
"lint-staged": "16.3.0",
"npm-run-all": "4.1.5",
- "webpack-remove-empty-scripts": "1.1.1"
+ "webpack-remove-empty-scripts": "1.1.1",
+ "webpack-watched-glob-entries-plugin": "3.2.0"
}
},
"node_modules/@ampproject/remapping": {
@@ -32202,6 +32203,68 @@
"node": ">=10.13.0"
}
},
+ "node_modules/webpack-watched-glob-entries-plugin": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/webpack-watched-glob-entries-plugin/-/webpack-watched-glob-entries-plugin-3.2.0.tgz",
+ "integrity": "sha512-XAzGyRBUqQxkA4ZLE71/nDxMrN34aCC2RKloLai4S3uhNT7le8ZuGvlWAms0sWf4hb1VJ6ODMqvznenA2i1oCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^13.0.6",
+ "glob-parent": ">=6.0.2"
+ },
+ "engines": {
+ "node": ">=18.0.0 <=25"
+ },
+ "optionalDependencies": {
+ "fsevents": "^2.3.3"
+ }
+ },
+ "node_modules/webpack-watched-glob-entries-plugin/node_modules/glob": {
+ "version": "13.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
+ "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/webpack-watched-glob-entries-plugin/node_modules/lru-cache": {
+ "version": "11.2.6",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
+ "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/webpack-watched-glob-entries-plugin/node_modules/path-scurry": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+ "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/websocket-driver": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
diff --git a/package.json b/package.json
index 92285bf..65d680f 100644
--- a/package.json
+++ b/package.json
@@ -45,11 +45,14 @@
"jest-silent-reporter": "0.6.0",
"lint-staged": "16.3.0",
"npm-run-all": "4.1.5",
- "webpack-remove-empty-scripts": "1.1.1"
+ "webpack-remove-empty-scripts": "1.1.1",
+ "webpack-watched-glob-entries-plugin": "3.2.0"
},
"scripts": {
"build:dev": "cross-env NODE_ENV=development npm-run-all 'build:!(dev|prod)'",
"build:prod": "cross-env NODE_ENV=production npm-run-all 'build:!(dev|prod)'",
+ "build:blocks": "wp-scripts build --webpack-copy-php --config ./node_modules/@wordpress/scripts/config/webpack.config.js --source-path=./assets/src/blocks --output-path=./assets/build/blocks",
+ "start:blocks": "wp-scripts start --webpack-copy-php --config ./node_modules/@wordpress/scripts/config/webpack.config.js --webpack-src-dir=./assets/src/blocks/ --output-path=./assets/build/blocks/",
"build:js": "wp-scripts build --experimental-modules",
"init": "./bin/init.js",
"lint:all": "npm-run-all --parallel lint:*",
diff --git a/webpack.config.js b/webpack.config.js
index 9e37796..50c188f 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -5,34 +5,30 @@ const fs = require( 'fs' );
const path = require( 'path' );
const CssMinimizerPlugin = require( 'css-minimizer-webpack-plugin' );
const RemoveEmptyScriptsPlugin = require( 'webpack-remove-empty-scripts' );
+const WebpackWatchedGlobEntries = require( 'webpack-watched-glob-entries-plugin' );
/**
* WordPress dependencies
*/
-const [ scriptConfig, moduleConfig ] = require( '@wordpress/scripts/config/webpack.config' );
+const [scriptConfig, moduleConfig] = require('@wordpress/scripts/config/webpack.config');
/**
* Read all file entries in a directory.
- * @param {string} dir Directory to read.
- * @return {Object} Object with file entries.
*/
-const readAllFileEntries = ( dir ) => {
+const readAllFileEntries = (dir) => {
const entries = {};
- if ( ! fs.existsSync( dir ) ) {
+ if (!fs.existsSync(dir)) {
return entries;
}
- if ( fs.readdirSync( dir ).length === 0 ) {
- return entries;
- }
+ fs.readdirSync(dir).forEach((fileName) => {
+ const fullPath = `${dir}/${fileName}`;
- fs.readdirSync( dir ).forEach( ( fileName ) => {
- const fullPath = `${ dir }/${ fileName }`;
- if ( ! fs.lstatSync( fullPath ).isDirectory() && ! fileName.startsWith( '_' ) ) {
- entries[ fileName.replace( /\.[^/.]+$/, '' ) ] = fullPath;
+ if (!fs.lstatSync(fullPath).isDirectory() && !fileName.startsWith('_')) {
+ entries[fileName.replace(/\.[^/.]+$/, '')] = fullPath;
}
- } );
+ });
return entries;
};
@@ -66,35 +62,57 @@ const sharedConfig = {
},
};
-// Generate a webpack config which includes setup for CSS extraction.
-// Look for css/scss files and extract them into a build/css directory.
+// CSS build
const styles = {
...sharedConfig,
- entry: () => readAllFileEntries( './assets/src/css' ),
- module: {
- ...sharedConfig.module,
+ entry: WebpackWatchedGlobEntries.getEntries(
+ [
+ path.resolve( __dirname, `assets/src/css/*.scss` ),
+ ],
+ {
+ ignore: [
+ path.resolve( __dirname, `assets/src/css/**/_*.scss` ),
+ ],
+ },
+ ),
+ output: {
+ ...sharedConfig.output,
+ path: path.resolve( process.cwd(), 'assets', 'build', 'css' ),
},
plugins: [
...sharedConfig.plugins.filter(
- ( plugin ) => plugin.constructor.name !== 'DependencyExtractionWebpackPlugin',
+ ( plugin ) => {
+ return plugin.constructor.name !== 'DependencyExtractionWebpackPlugin' && plugin.constructor.name !== 'CopyPlugin';
+ },
),
],
-
};
+// JS build
const scripts = {
...sharedConfig,
entry: {
- 'core-navigation': path.resolve( process.cwd(), 'assets', 'src', 'js', 'core-navigation.js' ),
+ ...sharedConfig.entry(),
+ ...WebpackWatchedGlobEntries.getEntries(
+ [
+ path.resolve( __dirname, `assets/src/js/*.js` ),
+ ],
+ {
+ ignore: [
+ path.resolve( __dirname, `assets/src/js/_*.js` ),
+ ],
+ },
+ )(),
},
};
+// module scripts.
const moduleScripts = {
...moduleConfig,
- entry: () => readAllFileEntries( './assets/src/js/modules' ),
+ entry: () => readAllFileEntries('./assets/src/js/modules'),
output: {
...moduleConfig.output,
- path: path.resolve( process.cwd(), 'assets', 'build', 'js', 'modules' ),
+ path: path.resolve(process.cwd(), 'assets', 'build', 'js', 'modules'),
filename: '[name].js',
chunkFilename: '[name].js',
},