Compiling CSS breaks/stops when an eslint rule is broken

How do you stop the compiler from breaking when an eslint rule is broken and instead just reeprint the error when running npm run dev

For example this appears tthe first time you run the command. if you have an error:

✘ resources/styles/main-nav.css
 26:31  ✖  Unexpected whitespace after "(" in a single-line function  function-parentheses-space-inside

But if you click save without resolving it the compiler breaks, and stops running:

(node:5967) UnhandledPromiseRejectionWarning: Error: TypeError: (0 , url_1.urlToHttpOptions) is not a function
    at Bud.default_1 (/Users/philiprudy/Work/multivisiondigital/wordpress/wp-content/themes/mvd/node_modules/@roots/sage/lib/cjs/hooks/event.compiler.done.js:31:15)
    at /Users/philiprudy/Work/multivisiondigital/wordpress/wp-content/themes/mvd/node_modules/@roots/bud-hooks/lib/cjs/Hooks/index.js:159:65
    at Array.reduce (<anonymous>)
    at Hooks.filter (/Users/philiprudy/Work/multivisiondigital/wordpress/wp-content/themes/mvd/node_modules/@roots/bud-hooks/lib/cjs/Hooks/index.js:159:29)
    at /Users/philiprudy/Work/multivisiondigital/wordpress/wp-content/themes/mvd/node_modules/@roots/bud-compiler/lib/cjs/Compiler/index.js:76:28
    at Hook.eval [as call] (eval at create (/Users/philiprudy/Work/multivisiondigital/wordpress/wp-content/themes/mvd/node_modules/tapable/lib/HookCodeFactory.js:19:10), <anonymous>:7:1)
    at /Users/philiprudy/Work/multivisiondigital/wordpress/wp-content/themes/mvd/node_modules/webpack/lib/MultiCompiler.js:97:22
    at _next1 (eval at create (/Users/philiprudy/Work/multivisiondigital/wordpress/wp-content/themes/mvd/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:27:1)
    at eval (eval at create (/Users/philiprudy/Work/multivisiondigital/wordpress/wp-content/themes/mvd/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:60:1)
    at MergedManifestWebpackPlugin.done (/Users/philiprudy/Work/multivisiondigital/wordpress/wp-content/themes/mvd/node_modules/@roots/merged-manifest-webpack-plugin/lib/cjs/index.js:84:20)
(node:5967) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 28)

This looks like it struggling with a css file so it might be a stylelint error: Search results for 'Stylelint error' - Roots Discourse

It’s a stylelint error, but if I fix the error and save, it doesn’t update, the stylelinit error just breaks the watch process. How do I stop that from happening?

It’s also happening to me. When a rule has been fixed after an error, the watching job seems to be OK, but you must stop and run yarn dev again to be able to see your css changes.
Can’t find a fix for now.

3 Likes

Any updates on the reason why this is happening and a possible fix?

On our end we need to disable bail option when running in dev.

You can apply this hook for now to only use bail when compiling with bud build. It should alleviate the issue somewhat:

app.hooks.on('bail', app.isProduction)

I don’t think it will fully fix it. Personally, I think stylelint is just kind of janky like this.

My recommendation is to disable stylelint-webpack-plugin's failOnError in development:

  app.extensions
    .get('stylelint-webpack-plugin')
    .setOption('failOnError', app.isProduction);

You’ll still see the errors emitted but they won’t trigger a fail state:

Maybe this should be the default behavior. I don’t know. We aren’t customizing the stylelint-webpack-plugin defaults in any way… I would think that the defaults would “just work”.

More drastically, you can disable stylelint entirely when running hot:

  app.extensions
    .get('stylelint-webpack-plugin')
    .set('when', app.isProduction)
5 Likes

Are you able to also tell where you are putting these lines of code exactly? Tried chaining this to the app in bud.config.js but that’s causing an error:

TypeError: app.use() requires a middleware function

Sure, here is the full config:

/**
 * @typedef {import('@roots/bud').Bud} bud
 *
 * @param {bud} app
 */
module.exports = async (app) => {
  app.extensions.get('stylelint-webpack-plugin').set('when', app.isProduction);

  app
    /**
     * Application entrypoints
     *
     * Paths are relative to your resources directory
     */
    .entry({
      app: ['@scripts/app', '@styles/app'],
      editor: ['@scripts/editor', '@styles/editor'],
    })

    /**
     * These files should be processed as part of the build
     * even if they are not explicitly imported in application assets.
     */
    .assets('images')

    /**
     * These files will trigger a full page reload
     * when modified.
     */
    .watch('resources/views/**/*', 'app/**/*')

    /**
     * Target URL to be proxied by the dev server.
     *
     * This is your local dev server.
     */
    .proxy('http://example.test')

    /**
     * Development URL
     */
    .serve('http://example.test:3000')

    /**
     * Generate WordPress `theme.json`
     *
     * @note This overwrites `theme.json` on every build.
     */
    .themeJson({
      color: {
        custom: false,
        customGradient: false,
      },
      custom: {
        spacing: {},
        typography: {
          'font-size': {},
          'line-height': {},
        },
      },
      spacing: {
        padding: true,
        units: ['px', '%', 'em', 'rem', 'vw', 'vh'],
      },
      typography: {
        customFontSize: false,
      },
    })

    /**
     * Set `theme.json` colors from `tailwind.config.js` values
     */
    .useTailwindColors();
};

That line doesn’t return bud, so you can’t chain it.

If you want to chain it there is a bud.tap utility function:

module.exports = async (app) => {
  app
    /**
     * Disable stylelint in development
     */
    .tap((app) =>
      app.extensions
        .get('stylelint-webpack-plugin')
        .set('when', app.isProduction),
  )
    
    /**
     * Application entrypoints
     *
     * Paths are relative to your resources directory
     */
    .entry({
      app: ['@scripts/app', '@styles/app'],
      editor: ['@scripts/editor', '@styles/editor'],
    })
   //...
}

Or, you could use bud.when:

bud.when(app.isProduction, app => {
  app.extensions.get('stylelint-webpack-plugin').set('when', false)
}) // this is chainable
1 Like

Just realized I didn’t answer the question about the hook specifically.

It actually does return bud. So, this is fine:

app
  .hooks.on('build.bail', app.isProduction)
  .tap((app) => 
    app.extensions.get('stylelint-webpack-plugin').set('when', app.isProduction)
  ) // returns bud

Couple other tips

You can destructure the tap object to ensure it stays readable:

app
  .tap(({hooks}) => hooks.on('build.bail', app.isProduction))
  .tap(({entry}) => entry('app', 'app.js')
  .tap(({log}) => log('extensions', app.extensions.getKeys())

You can also use bud.sequence as a shorthand if you’re chaining tap. This is the same as above:

app.sequence([
  ({hooks}) => hooks.on('build.bail', app.isProduction)
  ({entry}) => entry('app', 'app.js')
  ({log}) => log('extensions', app.extensions.getKeys())
]) // returns bud
2 Likes

We have forked webpack hot middleware for bud as development seems to have temporarily stalled over at the whm repo.

I think we’ve fixed the issues between whm and webpack 5 and I also believe these fixes apply to this issue.

We’re finishing testing now but I anticipate these fixes to be included in the release of 5.7.0.

2 Likes

@kellymears’s solution doesn’t work for my workflow with latest bud/sage.
I have similar problem. Watch in browser is break when I have stylelint error, but when I refresh page manually by cmd+r browser watch is back to work.

But I found other solution. I just add below code to bud config:

.hooks.on('build.optimization.emitOnErrors', () => true)

FWIW here’s the syntax I’m using in Sage 6.3.5

app.extensions.get('@roots/bud-stylelint')
  .setOption('failOnError', app.isProduction)
  .setOption('failOnWarning', app.isProduction);
2 Likes

Doesn’t work on 6.5.1. After SassError I got “error Command failed with exit code 1.” and I must run again yarn dev.

@Liskownik I ended up switching to this for my use case but not sure that will help:

bud.extensions.get('@roots/bud-stylelint')
  .setOption('failOnError', false)
  .setOption('failOnWarning', false);

I got SassError, not stylelint warn/error. This code fixed only stylelint, my mistake :man_facepalming: When I have errors from stylelint it is works ok, but when e.g. I write “background: ;” (without value) I got SassError and crash bud. Bud should catch the error to prevent crashing compiling…

Yeah, you’re right. The problem is that sass-loader throws an error without using the webpack compilation error API (at least, I think). This is why postcss-loader doesn’t throw hard even though they are both processed the same way on bud’s end.

Anyway, set up an issue to track this: [bug] @roots/bud-sass throws error in `development`; breaks hmr · Issue #1798 · roots/bud · GitHub
And a PR to fix it: fix: #1798 @roots/bud-sass dev errors by kellymears · Pull Request #1799 · roots/bud · GitHub

Includes an integration test specifically to prevent a regression here in the future: bud/error.test.ts at 53a6cc5fb79ce4f614bb789881ee8d16dfe5f55a · roots/bud · GitHub

I’ll try to get it released soon as I can.

3 Likes

Released this fix as v6.5.3. Thanks for the feedback @Liskownik.

3 Likes

@tedw i almost forgot to mention that bud.stylelint exposes @roots/bud-stylelint now. It should generally provide better intellisense and maybe reveal additional useful config options:

It also has a couple additional convenience methods: bud.stylelint.failOnError and bud.stylelint.failOnWarning, although they don’t provide much additional utility beyond what setOption does.

1 Like