node.js and webpack throws error when trying to setup node to watch theme folder for saved changes

The question:

Ok, so I have been getting this error thrown out when I try to run my npm run devFast command, so it can watch my theme for saved changes.

I think it has to do something with my file structure, some how its not reading the wordpress css file that I have copied over to the css folder so that the scripts.js file can import the css file into scripts.js to refresh my browser automatically when I make changes to my files.

I am not sure how to resolve this if its not my file structure at fault. I have researched exhaustively online, but I have not found anything.

Error in question:

PS C:wamp64wwwworkshop_01wp-contentthemesworkshop_01> npm run devFast

> [email protected] devFast C:wamp64wwwworkshop_01wp-contentthemesworkshop_01
> webpack serve

i 「wds」: Project is running at http://localhost:3000/
i 「wds」: webpack output is served from http://localhost:3000/
i 「wds」: Content not from webpack is served from C:wamp64wwwworkshop_01wp-contentthemesworkshop_01
× 「wdm」: asset bundled.js 405 KiB [emitted] (name: scripts) 1 related asset
runtime modules 25.9 KiB 13 modules
modules by path ./node_modules/ 345 KiB
  modules by path ./node_modules/webpack-dev-server/client/ 20.9 KiB 10 modules
  modules by path ./node_modules/webpack/hot/ 4.46 KiB 5 modules
  modules by path ./node_modules/html-entities/lib/*.js 61 KiB 5 modules
  modules by path ./node_modules/url/ 37.4 KiB 3 modules
  modules by path ./node_modules/querystring/*.js 4.51 KiB
    ./node_modules/querystring/index.js 127 bytes [built] [code generated]
    ./node_modules/querystring/decode.js 2.34 KiB [built] [code generated]
    ./node_modules/querystring/encode.js 2.04 KiB [built] [code generated]
modules by path ./css/*.css 1.51 KiB
  ./css/style.css 1.48 KiB [built] [code generated]
  ./node_modules/css-loader/dist/cjs.js?url=false!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[0].use[2]!./css/style.css 39 bytes [built] [code generated] [1 error]        
./js/scripts.js 148 bytes [built] [code generated]

ERROR in ./css/style.css (./node_modules/css-loader/dist/cjs.js?url=false!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[0].use[2]!./css/style.css)
Module build failed (from ./node_modules/postcss-loader/dist/cjs.js):
ValidationError: Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'plugins'. These properties are valid:
   object { postcssOptions?, execute?, sourceMap?, implementation? }
    at validate (C:wamp64wwwworkshop_01wp-contentthemesworkshop_01node_moduleswebpacknode_modulesschema-utilsdistvalidate.js:104:11)
    at Object.getOptions (C:wamp64wwwworkshop_01wp-contentthemesworkshop_01node_moduleswebpacklibNormalModule.js:529:19)
    at Object.loader (C:wamp64wwwworkshop_01wp-contentthemesworkshop_01node_modulespostcss-loaderdistindex.js:40:24)
 @ ./css/style.css 2:12-168 9:17-24 13:7-21 45:20-34 49:6-59:7 50:38-52 56:26-40 58:21-28 68:15-29 47:4-60:5
 @ ./js/scripts.js 1:0-26

webpack 5.38.1 compiled with 1 error in 4433 ms
i 「wdm」: Failed to compile.

Here are the additional outputs for my package.json, webpack.config.js, scripts.js, and file structure:


  "name": "workshop_01",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "npm-run-all -p devFast buildWatch",
    "devFast": "webpack serve",
    "buildWatch": "webpack --watch",
    "build": "webpack",
    "test": "echo "Error: no test specified" && exit 1"
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/core": "^7.14.5",
    "@babel/preset-env": "^7.14.5",
    "@babel/preset-react": "^7.14.5",
    "@glidejs/glide": "^3.4.1",
    "autoprefixer": "^10.2.6",
    "axios": "^0.21.1",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^4.0.0-alpha.0",
    "css-loader": "^5.2.6",
    "cssnano": "^5.0.6",
    "fs-extra": "^10.0.0",
    "jquery": "^3.6.0",
    "mini-css-extract-plugin": "^1.6.0",
    "normalize.css": "^8.0.1",
    "npm-run-all": "^4.1.5",
    "postcss-color-function": "^4.1.0",
    "postcss-hexrgba": "^2.0.1",
    "postcss-import": "^14.0.2",
    "postcss-loader": "^6.1.0",
    "postcss-mixins": "^8.1.0",
    "postcss-nested": "^5.0.5",
    "postcss-simple-vars": "^6.0.3",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "style-loader": "^2.0.0",
    "webpack": "^5.38.1",
    "webpack-cli": "^4.7.2",
    "webpack-dev-server": "^3.11.2",
    "webpack-manifest-plugin": "^3.1.1"


  SUPER IMPORTANT: This config assumes your theme folder is named
  exactly 'fictional-university-theme' and that you have a folder
  inside it named 'bundled-assets' - If you'd like to adapt this
  config to work with your own custom folder structure and names
  be sure to adjust the publicPath value on line #116. You do NOT
  need to update any of the other publicPath settings in this file,
  only the one on line #116.

const currentTask = process.env.npm_lifecycle_event
const path = require("path")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const ManifestPlugin = require("webpack-manifest-plugin")
const fse = require("fs-extra")

const postCSSPlugins = [require("postcss-import"), require("postcss-mixins"), require("postcss-simple-vars"), require("postcss-nested"), require("postcss-hexrgba"), require("postcss-color-function"), require("autoprefixer")]

class RunAfterCompile {
  apply(compiler) {
    compiler.hooks.done.tap("Update functions.php", function () {
      // update functions php here
      const manifest = fse.readJsonSync("./bundled-assets/manifest.json")

      fse.readFile("./functions.php", "utf8", function (err, data) {
        if (err) {

        const scriptsRegEx = new RegExp("/bundled-assets/scripts.+?'", "g")
        const vendorsRegEx = new RegExp("/bundled-assets/vendors.+?'", "g")
        const cssRegEx = new RegExp("/bundled-assets/styles.+?'", "g")

        let result = data.replace(scriptsRegEx, `/bundled-assets/${manifest["scripts.js"]}'`).replace(vendorsRegEx, `/bundled-assets/${manifest["vendors~scripts.js"]}'`).replace(cssRegEx, `/bundled-assets/${manifest["scripts.css"]}'`)

        fse.writeFile("./functions.php", result, "utf8", function (err) {
          if (err) return console.log(err)

let cssConfig = {
  test: /.css$/i,
  use: ["css-loader?url=false", { loader: "postcss-loader", options: { plugins: postCSSPlugins } }]

let config = {
  entry: {
    scripts: "./js/scripts.js"
  plugins: [],
  module: {
    rules: [
        test: /.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-react", ["@babel/preset-env", { targets: { node: "12" } }]]

if (currentTask == "devFast") {
  config.devtool = "source-map"
  config.output = {
    filename: "bundled.js",
    publicPath: "http://localhost:3000/"
  config.devServer = {
    before: function (app, server) {
        If you want the browser to also perform a traditional refresh
        after a save to a JS file you can modify the line directly
        below this comment to look like this instead. I'm using this approach
        instead of just disabling Hot Module Replacement beacuse this way our
        CSS updates can still happen immediately without a page refresh.
        If you're using a slower computer and the new bundle is not ready
        by the time this is reloading the browser you can always just set the 
        "hot" property a few lines below this to false instead of true. That
        will work on all computers and the only trade off is the browser will
        perform a traditional refresh even for CSS changes as well.

      // server._watch(["./**/*.php", "./**/*.js"])
      server._watch(["./**/*.php", "!./functions.php"])
    public: "http://localhost:3000",
    publicPath: "http://localhost:3000/",
    disableHostCheck: true,
    contentBase: path.join(__dirname),
    contentBasePublicPath: "http://localhost:3000/",
    hot: true,
    port: 3000,
    headers: {
      "Access-Control-Allow-Origin": "*"
  config.mode = "development"

if (currentTask == "build" || currentTask == "buildWatch") {
  config.output = {
    publicPath: "/wp-content/themes/workshop_01/bundled-assets/",
    filename: "[name].[chunkhash].js",
    chunkFilename: "[name].[chunkhash].js",
    path: path.resolve(__dirname, "bundled-assets")
  config.mode = "production"
  config.optimization = {
    splitChunks: { chunks: "all" }
  config.plugins.push(new CleanWebpackPlugin(), new MiniCssExtractPlugin({ filename: "styles.[chunkhash].css" }), new ManifestPlugin({ publicPath: "" }), new RunAfterCompile())

module.exports = config


import "../css/style.css"

// Allow new JS and CSS to load in browser without a traditional page refresh
if ( {

node.js and webpack throws error when trying to setup node to watch theme folder for saved changes

Can anyone see any issues?
I have the latest package versions too, so all I can think of is my folder directories are the issue.

Please help me.

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

It seems to be a syntax error.

The options should be passed inside postcssOption according to the webpack documentation.

Try replacing this line:

  use: ["css-loader?url=false", { loader: "postcss-loader", options: { plugins: postCSSPlugins } }]

With this:

  use: ["css-loader?url=false", { loader: "postcss-loader", options: { postcssOptions : { plugins: postCSSPlugins} } }]

All methods was sourced from or, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment