To nie będzie kolejny wpis o tym jak zacząć tworzenie nowych bloków w WordPressie za pomocą create-guten-block. Dla mnie osobiście przełączanie się pomiędzy wtyczką a motywem, między webpackiem a gulpem byłoby nieco uciążliwe. Zapragnąłem mieć wszystko w jednym miejscu.
I tak zaczęło się poszukiwanie rozwiązań, które nie dawały mi 100% satysfakcji, gdyż nie były stabilne. Za to bardzo obiecująca jest poniższa konfiguracja. Ale od początku…
Celem jaki sobie postawiłem, było umożliwienie tworzenia bloków Gutenberga w dowolnym motywie i utworzenie przy tym stabilnego środowiska developerskiego. Podszedłem do tego, jak to w przypadku historyka, jako badacz, a nie ekspert. Istotą problemu, z jakim musiałem się zmierzyć to umożliwienie pisania kodu w JSX przy jednoczesnej kompilacji go do standardu ES5. Najpierw były nieudane próby z gulpem. Potem idąc torem create-guten-block próbowałem stworzyć motyw w oparciu o webpack. Też bezskutecznie. Działał zbyt wolno. Okazało się, że idealnym rozwiązaniem dla mnie było połączenie gulpa z webpackiem.
Ale zaraz. Jak tworzyć bloki w motywie? Przecież wszyscy rekomendują tworzenie wtyczki. A czy nie fajnie byłoby mieć wszystko w jednym? Do zrobienia tej konfiguracji pomogło mi właśnie narzędzie create-guten-block.
Przekształcając funkcję i umieszczając ją do pliku functions.php. Umożliwia ona rejestrację zarówno wynikowego skryptu (app.js) jak i styli.
wp_enqueue_script( 'wp-api' );
// Register block editor script for backend.
wp_enqueue_script(
'blocks-js', // Handle.
get_template_directory_uri() . '/dist/app.js',
array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor' ), // Dependencies, defined above.
null, // filemtime( plugin_dir_path( __DIR__ ) . 'dist/blocks.build.js' ), // Version: filemtime — Gets file modification time.
true // Enqueue the script in the footer.
);
function blocks_assets() {
// Styles.
wp_enqueue_style(
'blocks-css', // Handle.
get_template_directory_uri() . '/blocks.css',
array( 'wp-editor', 'wp-edit-blocks' ) // Dependency to include the CSS after it.
// filemtime( plugin_dir_path( __DIR__ ) . 'dist/blocks.style.build.css' ) // Version: File modification time.
);
}
// Hook: Frontend assets.
add_action( 'enqueue_block_assets', 'blocks_assets' );
register_block_type(
'cgb/block-lub-blocks', array(
// Enqueue blocks.build.js in the editor only.
'editor_script' => 'blocks-js',
'style' => 'blocks-css',
'editor_style' => 'blocks-css',
)
);
Następnie musimy zainstalować w folderze naszego motywu potrzebne paczki. Tworzymy package.json:
{
"name": "wp-lubstarterdev",
"version": "1.0.0",
"description": "Lubstarter Dev Theme Sass Compiler",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Biuro REKLAMA",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"@fortawesome/fontawesome-free": "^5.9.0",
"babel-core": "^6.26.3",
"babel-loader": "^8.0.6",
"babel-preset-es2015": "^6.24.1",
"babel-preset-latest": "^6.24.1",
"babel-preset-react": "^6.24.1",
"bootstrap": "^4.3.1",
"browser-sync": "^2.26.7",
"del": "^3.0.0",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^5.0.0",
"gulp-concat": "^2.6.1",
"gulp-connect-php": "^1.0.2",
"gulp-cssnano": "^2.1.3",
"gulp-jshint": "^2.1.0",
"gulp-open": "^3.0.1",
"gulp-ruby-sass": "^3.0.0",
"gulp-sort": "^2.0.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-uglify": "^3.0.2",
"gulp-util": "^3.0.8",
"gulp-zip": "^4.2.0",
"node-bin-setup": "^1.0.6",
"popper.js": "^1.14.7",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"run-sequence": "^2.2.1",
"sequence": "^3.0.0",
"webpack": "^4.41.2",
"webpack-stream": "^5.2.1"
},
"dependencies": {
"@fancyapps/fancybox": "^3.5.6",
"animate.css": "^3.7.0",
"axios": "^0.19.0",
"jquery": "^3.4.1",
"jshint": "^2.10.1",
"owl.carousel": "^2.3.4",
"rellax": "^1.8.0",
"wow.js": "^1.2.2"
}
}
Następnie za pomocą wiersza poleceń wpisujemy:
npm install
i klikamy ENTER.
Teraz pora na konfigurację pliku gulpfile.js:
'use strict';
var gulp = require('gulp'),
gutil = require('gulp-util'),
cssnano = require('gulp-cssnano'),
sass = require('gulp-ruby-sass'),
sort = require('gulp-sort'),
runSequence = require('run-sequence'),
connect = require('gulp-connect-php'),
sourcemaps = require('gulp-sourcemaps'),
autoprefixer = require('gulp-autoprefixer'),
concat = require('gulp-concat'),
uglify = require('gulp-uglify'),
webpack = require('webpack'),
webpackStream = require('webpack-stream'),
browserSync = require('browser-sync').create();
var supported = [
'last 2 versions',
'safari >= 8',
'ie >= 10',
'ff >= 20',
'ios 6',
'android 4'
];
var build_files = [
'**',
'!node_modules',
'!node_modules/**',
'!build',
'!build/**',
'!src',
'!src/**',
'!.git',
'!.git/**',
'!package.json',
'!.gitignore',
'!gulpfile.js',
'!.editorconfig',
'!.jshintrc'
];
// Defining base paths
var basePaths = {
js: './js/',
node: './node_modules/',
dev: './src/',
css: './css/'
};
gulp.task('browser-sync', function () {
browserSync.init({
proxy: "localhost/blocks",
browser: "google chrome"
});
});
gulp.task('webpacks', function () {
return gulp.src('blocks/src/app.js')
.pipe(webpackStream({
config: require('./app.config.js'),
watch: true
}))
.pipe(gulp.dest('dist/'))
.pipe(browserSync.stream())
});
gulp.task('sass', () =>
sass('src/sass/**/*.scss', { sourcemap: true })
.pipe(autoprefixer())
.on('error', sass.logError)
.pipe(sourcemaps.write())
.pipe(cssnano({
autoprefixer: { browsers: supported, add: true }
}))
.pipe(sourcemaps.write('maps', {
includeContent: false,
sourceRoot: 'src/sass'
}))
.pipe(gulp.dest('./'))
.pipe(browserSync.stream())
);
gulp.task('watch', function () {
gulp.watch('src/sass/**/*.scss', gulp.series('sass'));
gulp.watch('blocks/src/**/*.js', gulp.series('webpacks'));
gulp.watch('**/*.php').on('change', function () {
browserSync.reload();
});
});
gulp.task('copy-assets', function () {
var stream = gulp.src(basePaths.node + 'bootstrap/dist/js/**/*.js')
.pipe(gulp.dest(basePaths.js + 'bootstrap'));
gulp.src(basePaths.node + 'bootstrap/dist/css/**/*.css')
.pipe(gulp.dest(basePaths.css + 'bootstrap'));
return stream;
});
gulp.task('copyfonts', function () {
var stream = gulp.src(basePaths.node + '@fortawesome/fontawesome-free/webfonts/**/*.{ttf,woff,woff2,eof,svg}')
.pipe(gulp.dest('./fonts/fontawesome'));
gulp.src(basePaths.node + '@fortawesome/fontawesome-free/css/**/*.css')
.pipe(gulp.dest(basePaths.css + 'fontawesome'));
return stream;
});
gulp.task('copypopper', function () {
var stream = gulp.src(basePaths.node + 'popper.js/dist/umd/popper.min.js')
.pipe(gulp.dest('./js'));
gulp.src(basePaths.node + 'popper.js/dist/umd/popper.js')
.pipe(gulp.dest('./js'));
return stream;
});
gulp.task('owl', function () {
var stream = gulp.src(basePaths.node + 'owl.carousel/dist/owl.carousel.min.js')
.pipe(gulp.dest('./js'));
gulp.src(basePaths.node + 'owl.carousel/dist/assets/owl.carousel.min.css')
.pipe(gulp.dest('./css'));
return stream;
});
gulp.task('animate', function () {
var stream = gulp.src(basePaths.node + 'animate.css/animate.min.css')
.pipe(gulp.dest('./css'));
return stream;
});
gulp.task('rellax', function () {
var stream = gulp.src(basePaths.node + 'rellax/rellax.min.js')
.pipe(gulp.dest('./js'));
return stream;
});
gulp.task('wow', function () {
var stream = gulp.src(basePaths.node + 'wow.js/dist/wow.min.js')
.pipe(gulp.dest('./js'));
return stream;
});
gulp.task('fancybox', function () {
var stream = gulp.src(basePaths.node + '@fancyapps/fancybox/dist/jquery.fancybox.min.js')
.pipe(gulp.dest('./js'));
gulp.src(basePaths.node + '@fancyapps/fancybox/dist/jquery.fancybox.min.css')
.pipe(gulp.dest('./css'));
return stream;
});
var defl = gulp.parallel(['sass', 'webpacks', 'watch', 'browser-sync']);
var ini = gulp.parallel(['copy-assets', 'copyfonts', 'copypopper', 'animate', 'wow', 'owl', 'rellax', 'fancybox']);
gulp.task(defl);
gulp.task('default', defl);
gulp.task(ini);
gulp.task('init', ini);
Jak widać w kopilacji z JSX do ES5 pomagają takie pluginy jak webpack i webpack-stream, które „łączą się” z plikiem app.config.js i dają wynikowy kod ES5 w dist/app.js, przy jednoczesnym podglądzie poprawności kodu (watch). W moim motywie bloki Gutenberga w JSX tworzę w folderze „blocks”. O jego strukturze napiszę w niedalekiej przyszłości.
A właśnie. Aby wszystko zadziałało, musimy jeszcze utworzyć wspomniany app.config.js. To tam dzieje się ta MAGIA.
module.exports = {
output: {
filename: 'app.js',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env',
'@babel/preset-react'],
plugins: [
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-proposal-class-properties"
]
},
},
],
},
watch: true
};
To jest nic innego jak część kofiguracji webpacka. W nazwie dałem app, tak dla zmyłki 🙂
Oczywiście przy konfiguracji swoich motywów, musicie wziąć pod uwagę strukturę folderów. Ja bazowałem na motywie twenty sixteen. Rozwiązanie jest w fazie testów. Liczę też na waszą pomoc i wskazówki.