Skip to content

Commit

Permalink
Merge pull request #195 from 10up/feature/refine-asset-handeling
Browse files Browse the repository at this point in the history
Refine asset handling to better reflect modern block development
  • Loading branch information
fabiankaegy authored Aug 1, 2022
2 parents 677debb + 940821a commit f2fc53b
Show file tree
Hide file tree
Showing 27 changed files with 646 additions and 1,332 deletions.
5 changes: 5 additions & 0 deletions .changeset/short-pets-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"10up-toolkit": minor
---

Refine the way block assets get handled. 10up-toolkit will now create Webpack entrypoints for any assets that are defined in any block.json files automatically for you. So no need to manually adding manual entrypoints per block.
1,638 changes: 443 additions & 1,195 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/toolkit/config/__tests__/webpack-basic-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ describe('webpack.config.js', () => {
'10up-toolkit': {
entry: entryBuildFiles,
paths: {
blocksDir: './includes2/blocks/',
srcDir: './assets2/',
cssLoaderPaths: ['./assets2/css', './includes2/blocks'],
copyAssetsDir: './assets2/',
Expand Down Expand Up @@ -178,6 +179,7 @@ describe('webpack.config.js', () => {
'10up-toolkit': {
entry: entryBuildFiles,
paths: {
blocksDir: './includes2/blocks/',
srcDir: './assets2/',
cssLoaderPaths: ['./assets2/css', './includes2/blocks'],
copyAssetsDir: './assets2/',
Expand Down
4 changes: 2 additions & 2 deletions packages/toolkit/config/filenames.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ module.exports = {
js: 'js/[name].js',
jsChunk: 'js/[name].[contenthash].chunk.js',
css: 'css/[name].css',
block: 'blocks/[name]/editor.js',
blockCSS: 'blocks/[name]/editor.css',
block: 'blocks/[name].js',
blockCSS: 'blocks/[name].css',
};
1 change: 1 addition & 0 deletions packages/toolkit/config/paths.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ module.exports = {
srcDir: './assets/',
cssLoaderPaths: ['./assets/css', './includes/blocks'],
copyAssetsDir: './assets/',
blocksDir: './includes/blocks/',
};
4 changes: 2 additions & 2 deletions packages/toolkit/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const {
getModules,
getResolve,
getTarget,
getPerfomance,
getPerformance,
getDevServer,
} = require('./webpack');

Expand Down Expand Up @@ -59,7 +59,7 @@ module.exports = {
target: getTarget(config),
resolve: getResolve(config),
externals: getExternals(config),
performance: getPerfomance(config),
performance: getPerformance(config),
module: getModules(config),
plugins: getPlugins(config),
stats: getStats(config),
Expand Down
20 changes: 15 additions & 5 deletions packages/toolkit/config/webpack/__tests__/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ describe('entry module function', () => {
it('returns project mode entry config', () => {
const buildFiles = { entry: 'entry.js' };
expect(
entry({ isPackage: false, packageConfig: {}, projectConfig: {}, buildFiles }),
entry({
isPackage: false,
packageConfig: {},
projectConfig: { paths: { blocksDir: './includes2/blocks/' } },
buildFiles,
}),
).toEqual(buildFiles);
});

it('returns package mode entry config', () => {
const buildFiles = { entry: 'entry.js' };
expect(
entry({ isPackage: true, packageConfig: {}, projectConfig: {}, buildFiles }),
entry({
isPackage: true,
packageConfig: {},
projectConfig: { paths: { blocksDir: './includes2/blocks/' } },
buildFiles,
}),
).toEqual(buildFiles);

expect(
Expand All @@ -24,7 +34,7 @@ describe('entry module function', () => {
umd: 'index.umd.js',
libraryName: 'LibraryName',
},
projectConfig: {},
projectConfig: { paths: { blocksDir: './includes2/blocks/' } },
buildFiles: [],
}),
).toEqual({
Expand Down Expand Up @@ -54,7 +64,7 @@ describe('entry module function', () => {
main: 'index.js',
libraryName: 'LibraryName',
},
projectConfig: {},
projectConfig: { paths: { blocksDir: './includes2/blocks/' } },
buildFiles: [],
}),
).toEqual({
Expand All @@ -76,7 +86,7 @@ describe('entry module function', () => {
main: 'index.js',
libraryName: 'LibraryName',
},
projectConfig: {},
projectConfig: { paths: { blocksDir: './includes2/blocks/' } },
buildFiles: [],
}),
).toEqual({
Expand Down
62 changes: 61 additions & 1 deletion packages/toolkit/config/webpack/entry.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,73 @@
const { readFileSync } = require('fs');
const { dirname, extname, join, resolve } = require('path');
const { sync: glob } = require('fast-glob');

const removeDistFolder = (file) => {
return file.replace(/(^\.\/dist\/)|^dist\//, '');
};

module.exports = ({
isPackage,
projectConfig: { devServer },
projectConfig: { devServer, paths, useBlockAssets },
packageConfig: { packageType, source, main, umd, libraryName },
buildFiles,
}) => {
let additionalEntrypoints = {};
if (useBlockAssets) {
const blocksSourceDirectory = resolve(process.cwd(), paths.blocksDir);

// get all block.json files in the blocks directory
const blockMetadataFiles = glob(`${blocksSourceDirectory}/**/block.json`, {
absolute: true,
});

// add any additional entrypoints we find in block.json filed to the webpack config
additionalEntrypoints = blockMetadataFiles.reduce((accumulator, blockMetadataFile) => {
// get all assets from the block.json file
const { editorScript, script, viewScript, style, editorStyle } = JSON.parse(
readFileSync(blockMetadataFile),
);

// generate a new entrypoint for each of the assets
[editorScript, script, viewScript, style, editorStyle]
.flat()
.filter((rawFilepath) => rawFilepath && rawFilepath.startsWith('file:')) // assets can be files or handles. we only want files
.forEach((rawFilepath) => {
// Removes the `file:` prefix.
const filepath = join(
dirname(blockMetadataFile),
rawFilepath.replace('file:', ''),
);

// get the entrypoint name from the filepath by removing the blocks source directory and the file extension
const entryName = filepath
.replace(extname(filepath), '')
.replace(blocksSourceDirectory, '')
.replace(/\\/g, '/');

// Detects the proper file extension used in the defined source directory.
const [entryFilepath] = glob(
`${blocksSourceDirectory}/${entryName}.([jt]s?(x)|?(s)css)`,
{
absolute: true,
},
);

if (!entryFilepath) {
// eslint-disable-next-line no-console
console.warn('There was no entry file found for', entryName);
return;
}

accumulator[entryName] = entryFilepath;
});
return accumulator;
}, {});
}

// merge the new entrypoints with the existing ones
Object.assign(buildFiles, additionalEntrypoints);

if (isPackage) {
const config = {};
const hasBuildFiles = Object.keys(buildFiles).length > 0;
Expand Down
4 changes: 2 additions & 2 deletions packages/toolkit/config/webpack/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const getOptimization = require('./optimization');
const getModules = require('./modules');
const getResolve = require('./resolve');
const getTarget = require('./target');
const getPerfomance = require('./perfomance');
const getPerformance = require('./performance');
const getDevServer = require('./devServer');

module.exports = {
Expand All @@ -20,6 +20,6 @@ module.exports = {
getModules,
getResolve,
getTarget,
getPerfomance,
getPerformance,
getDevServer,
};
2 changes: 1 addition & 1 deletion packages/toolkit/config/webpack/optimization.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = ({ isProduction, projectConfig: { hot, analyze } }) => {
terserOptions: {
parse: {
// We want terser to parse ecma 8 code. However, we don't want it
// to apply any minfication steps that turns valid ecma 5 code
// to apply any minification steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* Converts a size vlaue to bytes
* Converts a size value to bytes
*
* @param {number} size The size in kb
*
* @return {number} The size in bytes
* @returns {number} The size in bytes
*/
const kb = (size) => {
return size * 1024;
Expand Down
21 changes: 19 additions & 2 deletions packages/toolkit/config/webpack/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const { resolve } = require('path');
const CleanExtractedDeps = require('./plugins/clean-extracted-deps');
const TenUpToolkitTscPlugin = require('./plugins/tsc');
const NoBrowserSyncPlugin = require('./plugins/no-browser-sync');
Expand Down Expand Up @@ -37,8 +38,10 @@ module.exports = ({
wpDependencyExternals,
analyze,
hot,
useBlockAssets,
},
packageConfig: { style },
buildFiles,
}) => {
const hasReactFastRefresh = hot && !isProduction;

Expand Down Expand Up @@ -74,6 +77,8 @@ module.exports = ({
);
}

const blocksSourceDirectory = resolve(process.cwd(), paths.blocksDir);

return [
devServer &&
new HtmlWebpackPlugin({
Expand All @@ -93,7 +98,9 @@ module.exports = ({
return removeDistFolder(style);
}

return options.chunk.name.match(/-block$/) ? filenames.blockCSS : filenames.css;
return buildFiles[options.chunk.name].match(/\/blocks\//)
? filenames.blockCSS
: filenames.css;
},
chunkFilename: '[id].css',
}),
Expand All @@ -108,6 +115,16 @@ module.exports = ({
noErrorOnMissing: true,
context: path.resolve(process.cwd(), paths.copyAssetsDir),
},
useBlockAssets && {
from: `${blocksSourceDirectory}/**/block.json`,
context: path.resolve(process.cwd(), paths.blocksDir),
to: 'blocks/[path][name][ext]',
},
useBlockAssets && {
from: `${blocksSourceDirectory}/**/markup.php`,
context: path.resolve(process.cwd(), paths.blocksDir),
to: 'blocks/[path][name][ext]',
},
hasReactFastRefresh && {
from: fromConfigRoot('fast-refresh.php'),
to: '[path][name][ext]',
Expand All @@ -130,7 +147,7 @@ module.exports = ({
}),
// Fancy WebpackBar.
!hasReactFastRefresh && new WebpackBar(),
// dependecyExternals variable controls whether scripts' assets get
// dependencyExternals variable controls whether scripts' assets get
// generated, and the default externals set.
wpDependencyExternals &&
!isPackage &&
Expand Down
2 changes: 1 addition & 1 deletion packages/toolkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"bin": {
"10up-toolkit": "bin/10up-toolkit.js"
},
"bundleDependencies": [],
"dependencies": {
"@babel/core": "^7.17.8",
"@babel/eslint-parser": "^7.17.0",
Expand All @@ -38,6 +37,7 @@
"css-loader": "^6.7.1",
"cssnano": "^5.1.7",
"eslint-webpack-plugin": "^3.1.1",
"fast-glob": "^3.2.11",
"html-webpack-plugin": "^5.5.0",
"ignore-emit-webpack-plugin": "2.0.6",
"image-minimizer-webpack-plugin": "^3.2.3",
Expand Down
4 changes: 2 additions & 2 deletions packages/toolkit/test-utils/resolver.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const glob = require('glob');
const { sync: glob } = require('fast-glob');

let mapping = {};
// Looks for "module-resolution.json" files in all the `__tests__` directories
glob.sync(`${__dirname}/../**/__tests__/modules-resolution.json`).forEach((file) => {
glob(`${__dirname}/../**/__tests__/modules-resolution.json`).forEach((file) => {
// For each of them, merges them in the "mapping" object
mapping = { ...mapping, ...require(file) };
});
Expand Down
1 change: 1 addition & 0 deletions packages/toolkit/utils/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const getDefaultConfig = () => {
wpDependencyExternals:
typeof process.env.TENUP_NO_EXTERNALS === 'undefined' ||
!process.env.TENUP_NO_EXTERNALS,
useBlockAssets: false,
};
};

Expand Down
1 change: 1 addition & 0 deletions projects/10up-theme/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
define( 'TENUP_THEME_DIST_URL', TENUP_THEME_TEMPLATE_URL . '/dist/' );
define( 'TENUP_THEME_INC', TENUP_THEME_PATH . 'includes/' );
define( 'TENUP_THEME_BLOCK_DIR', TENUP_THEME_INC . 'blocks/' );
define( 'TENUP_THEME_BLOCK_DIST_DIR', TENUP_THEME_PATH . 'dist/blocks/' );

$is_local_env = in_array( wp_get_environment_type(), [ 'local', 'development' ], true );
$is_local_url = strpos( home_url(), '.test' ) || strpos( home_url(), '.local' );
Expand Down
48 changes: 27 additions & 21 deletions projects/10up-theme/includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,39 @@ function setup() {
* @return void
*/
function register_theme_blocks() {
// Filter the plugins URL to allow us to have blocks in themes with linked assets. i.e editorScripts
add_filter( 'plugins_url', __NAMESPACE__ . '\filter_plugins_url', 10, 2 );

// Register all the blocks in the theme
if ( file_exists( TENUP_THEME_BLOCK_DIST_DIR ) ) {
$block_json_files = glob( TENUP_THEME_BLOCK_DIST_DIR . '*/block.json' );

// Require custom blocks.
require_once TENUP_THEME_BLOCK_DIR . '/example-block/register.php';
// auto register all blocks that were found.
foreach ( $block_json_files as $filename ) {

// Call block register functions for each block.
Example\register();
$block_folder = dirname( $filename );

// Remove the filter after we register the blocks
remove_filter( 'plugins_url', __NAMESPACE__ . '\filter_plugins_url', 10, 2 );
}
$block_options = [];

/**
* Filter the plugins_url to allow us to use assets from theme.
*
* @param string $url The plugins url
* @param string $path The path to the asset.
*
* @return string The overridden url to the block asset.
*/
function filter_plugins_url( $url, $path ) {
$file = preg_replace( '/\.\.\//', '', $path );
return trailingslashit( get_stylesheet_directory_uri() ) . $file;
}
$markup_file_path = $block_folder . '/markup.php';
if ( file_exists( $markup_file_path ) ) {

// only add the render callback if the block has a file called markdown.php in it's directory
$block_options['render_callback'] = function( $attributes, $content, $block ) use ( $block_folder ) {

// create helpful variables that will be accessible in markup.php file
$context = $block->context;

// get the actual markup from the markup.php file
ob_start();
include $block_folder . '/markup.php';
return ob_get_clean();
};
};

register_block_type_from_metadata( $block_folder, $block_options );
};
};

}

/**
* Enqueue editor-only JavaScript/CSS for blocks.
Expand Down
Loading

0 comments on commit f2fc53b

Please sign in to comment.