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)
4 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