Skip to content

Commit

Permalink
Merge pull request #42 from humanmade/cra2
Browse files Browse the repository at this point in the history
Create React App v2
  • Loading branch information
roborourke authored Dec 3, 2020
2 parents d46b88f + b4ad6b9 commit 8669e79
Show file tree
Hide file tree
Showing 13 changed files with 12,347 additions and 7,898 deletions.
1 change: 1 addition & 0 deletions bin/react-wp-scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];

switch (script) {
// Overridden scripts.
case 'build':
case 'start': {
const result = spawn.sync(
'node',
Expand Down
13 changes: 12 additions & 1 deletion config/paths.js
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
module.exports = require( 'react-scripts/config/paths.js' );
const paths = require('react-scripts/config/paths.js');

module.exports = Object.assign( paths, {
// We point this to the template file in react-scripts to bypass the
// start and build script checks for the existence of this file. The
// index.html & public folder are of no use in a WP context.
appHtml: require.resolve('react-scripts/template/public/index.html'),
// Force the served path to a relative URL to generate the correct
// service worker precache manifest and also correct paths in the
// asset-manifest.json file.
servedPath: './',
} );
18 changes: 17 additions & 1 deletion config/webpack.config.prod.js
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
module.exports = require( 'react-scripts/config/webpack.config.prod.js' );
const config = require( 'react-scripts/config/webpack.config.prod.js' );
const { GenerateSW } = require( 'workbox-webpack-plugin' );
const applyWpConfig = require( '../overrides/applyWpConfig' );

// Remove default Workbox plugin.
config.plugins = config.plugins.filter( plugin => ! ( plugin instanceof GenerateSW ) );

// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the Webpack build without the navigation
// fallback.
config.plugins.push( new GenerateSW( {
clientsClaim: true,
exclude: [ /\.map$/, /asset-manifest\.json$/ ],
importWorkboxFrom: 'cdn',
} ) );

module.exports = applyWpConfig( config );
67 changes: 56 additions & 11 deletions loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,39 @@ function get_assets_list( string $directory ) {
$dev_assets = load_asset_file( $directory . 'asset-manifest.json' );
// Fall back to build directory if there is any error loading the development manifest.
if ( ! empty( $dev_assets ) ) {
return array_values( $dev_assets );
return filter_assets_list( $dev_assets );
}
}

$production_assets = load_asset_file( $directory . 'build/asset-manifest.json' );

if ( ! empty( $production_assets ) ) {
// Prepend "build/" to all build-directory array paths.
return array_map(
function( $asset_path ) { return 'build/' . $asset_path; },
array_values( $production_assets )
);
return filter_assets_list( array_map(
function( $asset_path ) use ( $directory ) {
// Use realpath to remove default relative path and confirm file exists.
$real_path = realpath( $directory . 'build/' . $asset_path );
return substr( $real_path, strpos( $real_path, 'build/' ) );
},
$production_assets
) );
}

return null;
}

/**
* Filter the assets to remove all async chunks, the service worker and precache manifest.
*
* @param array $assets
* @return array
*/
function filter_assets_list( array $assets ) {
return array_filter( $assets, function ( $asset_path ) {
return ! preg_match( '/\/\d+(\.[^\/]+)?\.chunk\.|precache-manifest|service-worker/', $asset_path );
} );
}

/**
* Infer a base web URL for a file system path.
*
Expand Down Expand Up @@ -130,10 +146,19 @@ function enqueue_assets( $directory, $opts = [] ) {
$defaults = [
'base_url' => '',
'handle' => basename( $directory ),
'scripts' => [
'react',
'react-dom',
],
'styles' => [],
];

$opts = wp_parse_args( $opts, $defaults );

// Ensure react & react-dom are dependencies.
$opts['scripts'] = array_merge( $opts['scripts'], [ 'react', 'react-dom' ] );
$opts['scripts'] = array_unique( $opts['scripts'] );

$assets = get_assets_list( $directory );

$base_url = $opts['base_url'];
Expand All @@ -150,21 +175,33 @@ function enqueue_assets( $directory, $opts = [] ) {
return;
}

// Make runtime / bundle first up.
uksort( $assets, function ( $asset_path ) {
if ( strstr( $asset_path, 'runtime' ) || strstr( $asset_path, 'bundle' ) ) {
return -1;
}
return 1;
} );

// There will be at most one JS and one CSS file in vanilla Create React App manifests.
$has_css = false;
foreach ( $assets as $asset_path ) {
$is_js = preg_match( '/\.js$/', $asset_path );
$is_css = preg_match( '/\.css$/', $asset_path );
$is_chunk = preg_match( '/\.chunk\./', $asset_path );
$is_js = preg_match( '/\.js$/', $asset_path );
$is_css = preg_match( '/\.css$/', $asset_path );
$is_runtime = preg_match( '/(runtime|bundle)/', $asset_path );

if ( ( ! $is_js && ! $is_css ) || $is_chunk ) {
if ( ! $is_js && ! $is_css ) {
// Assets such as source maps and images are also listed; ignore these.
continue;
}

// Set a dynamic handle as we can have more than one JS entry point.
// Treats the runtime file as primary to make setting dependencies easier.
$handle = $opts['handle'] . ( $is_runtime ? '' : '-' . sanitize_key( basename( $asset_path ) ) );

if ( $is_js ) {
wp_enqueue_script(
$opts['handle'],
$handle,
get_asset_uri( $asset_path, $base_url ),
$opts['scripts'],
null,
Expand All @@ -173,13 +210,20 @@ function enqueue_assets( $directory, $opts = [] ) {
} else if ( $is_css ) {
$has_css = true;
wp_enqueue_style(
$opts['handle'],
$handle,
get_asset_uri( $asset_path, $base_url ),
$opts['styles']
);
}
}

// Add the generated public path to the build directory.
wp_add_inline_script(
$opts['handle'],
sprintf( 'var %%PUBLIC_PATH_VAR%% = %s;', wp_json_encode( $base_url . '/build/' ) ),
'before'
);

// Ensure CSS dependencies are always loaded, even when using CSS-in-JS in
// development.
if ( ! $has_css ) {
Expand All @@ -192,6 +236,7 @@ function enqueue_assets( $directory, $opts = [] ) {
}
}


/**
* Display an overlay error when the React bundle cannot be loaded. It also stops the execution.
*
Expand Down
32 changes: 32 additions & 0 deletions overrides/applyWpConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const paths = require( '../config/paths' );
const HtmlWebpackPlugin = require( 'html-webpack-plugin' );
const InterpolateHtmlPlugin = require( 'react-dev-utils/InterpolateHtmlPlugin' );
const { DefinePlugin } = require( 'webpack' );

module.exports = config => {
const appNameVar = require(paths.appPackageJson).name.replace( /[\W]+/g, '' );

// Remove HTML file output plugins.
config.plugins = config.plugins.filter( plugin => ! ( plugin instanceof HtmlWebpackPlugin ) );
config.plugins = config.plugins.filter( plugin => ! ( plugin instanceof InterpolateHtmlPlugin ) );

// Change the optimization settings to only output async chunks. This means
// only the runtime and main bundle file need to be enqueued and helps us
// avoid the possiblity of enqueuing the wrong initial chunk file.
config.optimization.splitChunks.chunks = 'async';

// Set a default JSONP function name to avoid conflicts with other instances
// of react-wp-scripts using code splitting.
config.output.jsonpFunction = `${appNameVar}JSONP`;

// Set some useful externals based on what WP provides in v5.x.
config.externals = Object.assign( config.externals || {}, {
wp: 'wp',
react: 'React',
'react-dom': 'ReactDOM',
moment: 'moment',
lodash: 'lodash',
} );

return config;
};
Loading

0 comments on commit 8669e79

Please sign in to comment.