# Bud config for outputting multiple css files (from scss for ACF Blocks)

**URL:** https://discourse.roots.io/t/bud-config-for-outputting-multiple-css-files-from-scss-for-acf-blocks/22498
**Category:** bud
**Tags:** tip, sage10
**Created:** 2022-03-08T20:50:57Z
**Posts:** 22

## Post 1 by @slowrush — 2022-03-08T20:50:57Z

Hey thanks for all the hard work getting sage 10 + bud + acorn out the door :tada: really impressive!

I might be going about this all wrong? Hoping someone can point me in the right direction.

How would I add a directory of scss files to the Bud config to compile into separate CSS files (that will be enqueued by ACF Composer)? In pre-bud sage we have this in our Mix config:

```
// webpack.mix.js
const glob = require('glob');
...
// Export individual custom ACF Block .css files
glob.sync('resources/styles/blocks/custom/**/[^_]*.scss').forEach((file) => {
  mix.sass(file, 'styles/blocks');
});
...
```

```
// app/Blocks/ExampleBlock.php
...
public function enqueue() {
  wp_enqueue_style('sage/example-block.css', asset('styles/blocks/example-block.css')->uri(), false, null);
}
```

Any help/pointers very much appreciated! :slight_smile:

---

## Post 2 by @slowrush — 2022-03-08T20:55:35Z

ahah! I realised I was just missing a dot `./` in my relative styles path :man_facepalming: will post what I have in a sec in case it helps anyone or if anyone can improve on it

---

## Post 3 by @slowrush — 2022-03-08T21:02:02Z

```
// bud.config.js
const path = require('path');
const glob = require('glob');
...

const blockStyleFiles = {};
glob.sync('./resources/styles/blocks/**/[^_]*.scss').forEach((file) => {
  const name = path.basename(file).split('.')[0];
  blockStyleFiles[name] = file;
});

app
  .entry({
    ...{
      app: ['@scripts/app', '@styles/app'],
      editor: ['@scripts/editor', '@styles/editor'],
    },
    ...blockStyleFiles,
  })
```

```
// app/Blocks/ExampleBlock.php
...
public function enqueue() {
    bundle('example-block')->enqueue();
}
```

Couldn’t seem to get the alias `@styles/blocks/**/[^_]*.scss` to work with glob, guessing there’s some bud magic behind the scenes, but the relative path seems to work - any/all suggestions on how to tidy this up always appreciated

* * *

**Edit** : converted the blockStyleFiles array → object for the files to compile out separately + added enqueue snippet for ACF Block

---

## Post 4 by @ben — 2022-03-08T21:54:24Z

Nice, thank you for sharing!

---

## Post 5 by @zzzap — 2022-03-08T22:00:54Z

I’m using [ACF-Composer](https://github.com/Log1x/acf-composer) and here’s what the relevant pieces of my bud.config looks like

```
const glob = require('glob');
const path = require('path');

String.prototype.toKebabCase = function () {
  return this.match(
    /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g,
  ).join('-');
};

/**
   * Get list of entrypoints for blocks
   */
  const blocks = Object.assign(
    ...glob.sync('app/Blocks/*.php').map((block) => {
      const name = path.basename(block, '.php').toKebabCase().toLowerCase();
      const files = glob.sync(`./resources/{scripts,styles}/blocks/${name}.{js,scss}`);
      if (files.length !== 0) {
        return {[name]: files};
      }
    }),
  );

app.entry({
      app: ['scripts/app.js', 'styles/app.scss'],
      editor: ['scripts/editor.js', 'styles/editor.scss'],
      // ACF-Composer Blocks
      ...blocks,
    })
```

---

## Post 6 by @kellymears — 2022-03-08T23:51:53Z

Personally, I’d keep the blocks grouped by domain. It’s easier to work with.

My example uses this structure:

```
├── app
│ ├── blocks
│ │ ├── block-a.php
│ │ └── block-b.php
├── resources
│ ├── blocks
│ │ ├── block-a
│ │ │ ├── index.css
│ │ │ └── index.js
│ │ └── block-b
│ │ ├── index.css
│ │ └── index.js
```

Here is the asset mapper I came up with:

```
const {basename, join} = require('node:path');
const {globby} = require('@roots/bud-support');

const mappedAssets = async (dir) => {
  const assets = await globby.globby(`app/${dir}/*`);
  const rel = (s) => join(dir, basename(s, '.php'));
  const entry = (a, c) => ({...a, [c]: [c]});

  return assets.map(rel).reduce(entry, {});
};
```

And here is how it’s used:

```
app.entry({
  app: ['@scripts/app', '@styles/app'],
  editor: ['@scripts/editor', '@styles/editor'],  
   ...(await mappedAssets('blocks')),
});
```

You’ll end up with a dist directory that looks like this:

```
public
├── blocks
│ ├── block-a.4480df.js
│ └── block-b.4623bf.js
```

---

## Post 7 by @slowrush — 2022-03-09T00:13:48Z

amazing! thanks @kellymears :raised_hands: I’ll give this a go tomorrow

---

## Post 8 by @oxyc — 2022-03-09T01:53:25Z

You can also enqueue stylesheets automatically when blocks are used. Haven’t actually tried it with acf blocks but works with core and custom ones

```
// @see https://make.wordpress.org/core/2021/12/15/using-multiple-stylesheets-per-block/
    $manifest = config('assets.manifests.theme.assets');
    collect(json_decode(file_get_contents($manifest), true))
        ->keys()
        ->filter(fn ($file) => Str::startsWith($file, '/styles/blocks/'))
        ->map(fn ($file) => asset($file))
        ->each(function (Asset $asset) {
            $filename = pathinfo(basename($asset->path()), PATHINFO_FILENAME);
            [$collection, $blockName] = explode('-', $filename, 2);
            wp_enqueue_block_style("$collection/$blockName", [
                'handle' => "sage/block/$filename",
                'src' => $asset->uri(),
                'path' => $asset->path(),
            ]);
        });
```

---

## Post 9 by @Twansparant — 2022-09-27T08:12:54Z

> [@kellymears](#):
>
> Personally, I’d keep the blocks grouped by domain. It’s easier to work with.
> 
> My example uses this structure:
> 
> ```
> ├── app
> │ ├── blocks
> │ │ ├── block-a.php
> │ │ └── block-b.php
> ├── resources
> │ ├── blocks
> │ │ ├── block-a
> │ │ │ ├── index.css
> │ │ │ └── index.js
> │ │ └── block-b
> │ │ ├── index.css
> │ │ └── index.js
> ```

I would like to have the same structure and I was wondering how you achieved this in combination with [acf-composer](https://github.com/Log1x/acf-composer), or are you not using that?

Since it expects the block template to be in `/resources/views/blocks`?  
Should I use the `$view` variable or the `render` function?

The latter results in an error:

```
# Declaration of App\Blocks\MyBlock::render() must be compatible with Log1x\AcfComposer\Block::render($block, $content = '', $preview = false, $post_id = 0)
```

Thanks!

---

## Post 10 by @Log1x — 2022-09-27T11:11:08Z

> [@Twansparant](#):
>
> I would like to have the same structure and I was wondering how you achieved this in combination with [acf-composer](https://github.com/Log1x/acf-composer), or are you not using that?

I think Kelly was referring to a vanilla structure for blocks. What are you trying to achieve? Moving the blocks folder outside of `views/`? Right now it will always expect them to be in `views/` because that’s what Laravel’s `view()` function defaults to.

For putting them inside of a folder you should be able to set the [`$view`](https://github.com/Log1x/acf-composer/blob/b19eb3aefc36505ded477aff2539b8e74aee8880/src/Block.php#L77-L82) property to e.g. `blocks.block-a.index` to look for `index.blade.php` inside of the `resources/views/block-a` folder.

---

## Post 11 by @Twansparant — 2022-09-27T11:33:32Z

Thanks for you reply!

I think my goal is/was to have a separate folder for each ACF block in the resources folder, containing it’s own blade template, script & style files, instead of spreading those around in the views, scripts & styles folders.

If Laravel expects the block template to be in the views folder (which I understand completely), then it already defeats this purpose.

---

## Post 12 by @slowrush — 2022-09-27T11:49:59Z

aha! this was the missing piece of the puzzle for us, we’ve got all assets in their own folders but views were always left in `views/blocks/*` - going to have a play with `$view`:+1:

Semi-related, wondering if it’d be nicer to move each block (views + assets) to a plugin? :thinking:

---

## Post 13 by @Log1x — 2022-09-27T11:58:37Z

> [@slowrush](#):
>
> Semi-related, wondering if it’d be nicer to move each block (views + assets) to a plugin?

This is technically [already possible](https://github.com/Log1x/acf-composer/blob/b19eb3aefc36505ded477aff2539b8e74aee8880/src/AcfComposer.php#L125-L137) (unless it got broken at some point) but I haven’t actually used the implementation myself on production so it’s not battle tested or mentioned anywhere.

I will try to get the example repo for it polished/tested and made public when I can.

---

## Post 14 by @Davide_Prevosto — 2022-09-28T06:19:59Z

Thank you Brandon, I’ll wait for your example too. I suppose I missed something moving our blocks to a plugin ([Proposal: AcfComposer - relative path calculation · Issue #125 · Log1x/acf-composer · GitHub](https://github.com/Log1x/acf-composer/issues/125))

---

## Post 15 by @Twansparant — 2022-09-28T08:11:00Z

Following up on this, since you’re also the creator of the marvellous [log1x/poet](https://github.com/Log1x/poet) :slight_smile:

Would it also be possible to set the location of the block view in the poet [registering a block](https://github.com/Log1x/poet#registering-a-block) functionality, similar to the `$view` variable in [log1x/acf-composer](https://github.com/Log1x/acf-composer)?

`config/poet.php`:

```
return [
	'block' => [
		'domain/native-block'
	],
];
```

That way I can truly have this structure for native & acf blocks:

```
├── app
│ ├── Blocks
│ │ ├── AcfBlockA.php
│ │ └── AcfBlockB.php
├── resources
│ ├── views
│ │ ├── blocks
│ │ │ ├── acf-block-a
│ │ │ │ ├── index.css
│ │ │ │ ├── index.js
│ │ │ │ └── index.blade.php
│ │ │ └── acf-block-b
│ │ │ │ ├── index.css
│ │ │ │ ├── index.js
│ │ │ │ └── index.blade.php
│ │ │ └── native-block
│ │ │ │ ├── index.css
│ │ │ │ ├── index.js
│ │ │ │ └── index.blade.php
```

Right now the `native-block` view needs to be in the root of the views folder.

Thanks!

---

## Post 16 by @Log1x — 2022-09-28T11:48:33Z

> [@Twansparant](#):
>
> ```
> return [
> 'block' => [
> 'domain/native-block'
> ],
> ];
> ```

I think you can accomplish this [with](https://github.com/Log1x/poet/blob/3156e4aaf225028aa290feacf922de4ad48e5f1d/src/Modules/BlockModule.php#L42):

```
'block' => [
    'domain/native-block' => [
        'view' => 'blocks.native-block.index',
    ],
],
```

---

## Post 17 by @Twansparant — 2022-09-28T11:54:39Z

Brilliant! I misinterpreted that function.  
Thanks!

---

## Post 19 by @Fabio_Politi — 2022-10-14T15:10:02Z

@Log1x is there a way to generate with acf-composer a nested resources/views/blocks/acf-block-a foldering like the one mentioned before?  
Thx

---

## Post 20 by @kellymears — 2023-11-30T19:15:28Z

Updated my example for more recent versions of bud.js:

**Project structure:**

```
├── app
│ ├── blocks
│ │ ├── block-a.php
│ │ └── block-b.php
├── resources
│ ├── blocks
│ │ ├── block-a
│ │ │ ├── index.css
│ │ │ └── index.js
│ │ └── block-b
│ │ ├── index.css
│ │ └── index.js
```

**Utility function:**

```
import {basename, join} from 'node:path';
import glob from '@roots/bud-support/globby';

const mappedAssets = async (dir) => {
  const assets = await glob(`app/${dir}/*`);
  const rel = (s) => join(dir, basename(s, '.php'));
  const entry = (a, c) => ({...a, [c]: [c]});

  return assets.map(rel).reduce(entry, {});
};
```

**bud.config.js:**

```
app
    .entry('app', ['@scripts/app', '@styles/app'])
    .entry('editor', ['@scripts/editor', '@styles/editor'])
    .entry(await mappedAssets('blocks'))
```

**Output:**

```
public
├── css
│ └── blocks
│ └── block-a.ef46db.css
└── js
    └── blocks
        └── block-a.ac6f89.js
```

**entrypoints.json:**

```
{
  "blocks/block-a": {
    "js": [
      "js/runtime.c039ed.js",
      "js/blocks/block-a.ac6f89.js"
    ],
    "css": [
      "css/blocks/block-a.ef46db.css"
    ],
    "dependencies": []
  },
  "blocks/block-b": {
    "js": [
      "js/runtime.c039ed.js",
      "js/blocks/block-b.606d10.js"
    ],
    "dependencies": []
  }
}
```

---

## Post 21 by @chrisbakr — 2024-05-23T15:03:54Z

On bud 6.20.0 it only loads the .js file but not the .css. I also see in your entrypoints.json example that the css file is missing for “block-b”

I’ve updated the entry function and now it works as expected but I’m not sure it’s the best approach:

`const entry = (a, c) => ({ ...a, [c]: [c + '/index.css', c + '/index.js'] });`

---

## Post 22 by @xaykogeki — 2024-11-06T07:37:03Z

> [@kellymears](#):
>
> Updated my example for more recent versions of bud.js:

Hi,

Just have a few question with regards to the updated solution.

- I am getting an error with `import {basename, join} from 'node:path';` . The error is `Error parsing bud.config: require is not defined in ES module scope, you can use import instead` .
- I’ve installed `@roots/bud-support` via yarn in dev and tried running `yarn build`, but the error message this time too is `TypeError: The 'compilation' argument must be an instance of Compilation....`

I am in the same position as OP moving from laravel mix to bud and the config file does require a bit of tinkering to output multiple CSS from SCSS. Any advice is appreciated!
