Create-react-app (CRA) is nice to start a react project without messing with webpack and react setup. But CRA generates JS and CSS files with a hash in the name which makes it hard to include them in a Drupal library.

A libraries.yml entry looks like this:

module_name:
js:
js/script.js: {}
css:
component:
css/style.css: {}

To make it work with CRA we would have to update the file path each time we rebuild.

Hooks and a manifest to the rescue

To automate it we can use a file generated by CRA and a Drupal hook. But first we define the library with empty CSS and JS objects, We'll add the files from CRA later.

module_name:
js: {}
css:
component: {}

Luckily for us, CRA generates a asset-manifest.json file that is generated on build and looks like this:

{
"files": {
"main.css": "./static/css/main.e5894e93.css",
"main.js": "./static/js/main.1c36d7cb.js",
"index.html": "./index.html",
"main.e5894e93.css.map": "./static/css/main.e5894e93.css.map",
"main.1c36d7cb.js.map": "./static/js/main.1c36d7cb.js.map"
},
"entrypoints": [
"static/css/main.e5894e93.css",
"static/js/main.1c36d7cb.js"
]
}

Combining this file with the Drupal hook hook_library_info_alter() we can update the library definition when rebuilding the Drupal cache:

function module_name_library_info_alter(&$libraries, $extension) {
// The library name as defined in libraries.yml
$library_name = 'module_name';

if (isset($libraries[$library_name])) {
// Get the path to the module.
$pathResolver = \Drupal::service('extension.path.resolver');
$path = $pathResolver->getPath('module', $library_name);

// Load the manifest file and decode it.
$manifest = json_decode(file_get_contents($path . '/client/build/asset-manifest.json'), TRUE);

// Relative path to the build directory of CRA.
$cra_build_path = 'client/build/';

// Loop through the manifest and build the library definition.
foreach ($manifest['files'] as $key => $realpath) {
if (substr($key, -strlen('css')) === 'css') {
$libraries[$library_name]['css']['component'][$cra_build_path . ltrim($realpath, '\.\/')] = [];
}
else if (substr($key, -strlen('js')) === 'js') {
$libraries[$library_name]['js'][$cra_build_path . ltrim($realpath, '\.\/')] = [];
}
}
}
}

The code loads the asset-manifest, loops through each of the files and adds the files that passes the check to the library. We check for CSS and JS files since we don't want the index.html file and the map-files.

Now, we can clear the cache and the current CSS/JS files will be added to the library.