Compile singular SCSS without hashing

In need of having to create an additional CSS file in SASS, but we would like to avoid having to hash the compiled file. Is there a configuration I’m missing in bud.config.js where we can singularly bypass the .hash() function for that particular file that gets added to the entry?

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

You’re kind of going against the grain here – I’m not sure why you are hashing some files and not others? What are you trying to accomplish?

Check this out sometime if you’re curious why this is kind of a hassle:

Short summary: rollup doesn’t support this at all, and parcel is just as annoying as webpack. It’s likely not a priority because there aren’t many good reasons to do it. Sage is code split by default, which means that we use a bundler to intelligently and automatically determine what code is needed, and where, and when. You’re kind of throwing a wrench in that by asserting things about how the output should be structured.

Concerns aside, you totally can do this. I’ll give you two options. I would strongly recommend the first approach, but I will admit that the other option is less involved. Keep in mind that the recommended approach is future proof while the other approach may need to be adjusted in the future (if bud.js is still using webpack when webpack 6 releases we’ll likely abandon the tooling we use for css file naming, as webpack will handle it natively).

Recommended approach: two compilers

Sandbox: https://codesandbox.io/p/sandbox/blissful-flower-upg6tm

This is how multiple configurations are handled in Radicle. This is how it looks:

export default async (bud) => {
  await bud.make(`app`, async (app) => {
    app
      .setPath(`@dist`, `./dist/app`)
      .entry(`app`, [`app.js`, `app.css`])
      .hash()
  })

  await bud.make(`extra`, async (extra) => {
    extra
      .setPath(`@dist`, `./dist/extra`)
      .entry(`styles`, [`styles.css`])
  })
}

Put your existing config as-is into the top bud.make callback, and then do it again for your extra build step.

You can expect output like this:

╭─ app ./dist/app [8d37991148f3985ead32]
│
├─ entrypoints
│  └─ app
│     ├─ css/app.928106.css   32 bytes
│     └─ js/app.effa7b.js     71 bytes
│
╰─ compiled 3 modules (3 cached) in 411ms

╭─ extra ./dist/extra [ab3179d64128252f67c0]
│
├─ entrypoints
│  └─ styles
│     └─ css/styles.css  333 bytes
│
╰─ compiled 2 modules (2 cached) in 33ms

Added benefit: you can use the --filter flag to run the builds conditionally. So yarn bud build --filter app will only build the app code, which could be very nice if the other code doesn’t need to be built very often.

╭─ app ./dist/app [8d37991148f3985ead32]
│
├─ entrypoints
│  └─ app
│     ├─ css/app.928106.css   32 bytes
│     └─ js/app.effa7b.js     71 bytes
│
╰─ compiled 3 modules (3 cached) in 197ms

You will need to modify the config/assets.php config file in Sage and adjust your enqueue script (but I imagine you’re already doing that anyway). Again, Radicle can serve as a good reference implementation.

Option 2: Override webpack config

Sandbox: https://codesandbox.io/p/sandbox/determined-kalam-lcgx15

I have a feeling there is a nicer way to express this, but it works:

export default async (bud) => {
  bud.entry(`app`, [`app.js`, `app.css`])
  bud.entry(`styles`, [`styles.css`])

  bud.webpackConfig((config) => {
    const findPlugin = (plugin) =>
      plugin.constructor.name === `MiniCssExtractPlugin`

    const filename = ({ chunk }) =>
      chunk.name === `styles` ? `css/styles.css` : bud.relPath(`css/@name.css`)

    Object.assign(config.plugins.find(findPlugin).options, {
      filename,
    })

    return config
  })
}

This tracks down mini-css-extract-plugin and re-assigns the filename option. Our new callback returns the css file with no hash if the split chunk in question is named styles & the standard value otherwise.

In summary

  • It is worth interrogating why you are doing this. Clearly you feel hashing is import enough to leave enabled, so why is this asset being treated differently?
  • If you feel your reasons are valid you should consider using a multi compiler configuration
  • If you go with the webpackConfig callback I honestly wouldn’t blame you but you will likely need to adjust it in response to future changes.
1 Like

Hi, @kellymears. I need to acomplish this in order to set custom fonts to transactional emails. I’m using MJML with the font tag:

<mj-font name="Crimson Pro" href="https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700" />

Even though the example tag in the MJLM documentation does work, when I try to use the Crimson pro font, it doesn’t work. This is the default working example in the MJML docs:

<mj-font name="Raleway" href="https://fonts.googleapis.com/css?family=Raleway" />

So, I want to try with my own custom CSS font declaration. I have tried to copy the asset directly. It outputs a hashed file (email-font.d9efc6.css):

.assets(['email-css', 'css'])

Now I would like to try the approach you propose here, with two compilers. The problem is that I don’t know how to do it in the bud.config.js that comes by default in Sage.

I see that one compiler uses .hash() and the other does not. But in my bud.config.js the hash() method is not used, despite this the files are compiled with a hash in the name. I do not understand this.

My question is how to implement the two compilers, one with hash() and one without hash() in the bud.config.js that comes by default in Sage.

I’m using Bud 6.15.2

If it helps, this is my bud.config.js

This doesn’t answer your hashing question, but have you tried adding the css directly to your email inside <mj-style>...</mj-style> ? I would copy everything you need from the source code on this page: https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700

2 Likes

I hadn’t considered that possibility. It works! Thank you very much.