Notes
All PostsArchive
Android1App Store1C++1Cluster1CSS Specificity1CSS-in-JS1Emotion1Erlang1Input Method1iOS2JavaScript2Loaders1Networking1Node.js1Observables1Prerendering1Qt1React2React Native2RxJS2String Processing1WebEngine1Webpack2Windows2
© 2026 HUANG Cheng
All Posts
Qt and React Hybrid Development
Problem and Exploration A recent project posed an interesting question: how do you render AI chat responses with a typewriter effect inside a Qt app? One…
Feb 18, 2025
Detecting App Store Updates in React Native
Sometimes you ship an exciting new feature and want to prompt users to update right away. NetEase Cloud Music — a React Native app — does exactly this.…
Nov 15, 2024
A Little Pitfall in Erlang String Handling
I stumbled upon echo.opera.com — a service that prints out HTTP request headers — and thought it would be fun to build my own. After implementing echo_rs in…
Sep 26, 2024
Recursion in RxJS
The requirement: after a user selects or photographs an image, send it to an image-recognition API. The API rejects images over a certain size and returns…
Aug 1, 2024
The Mystery Behind RxJS iif
A common pattern in business logic: branch on a precondition to decide which API to call. For order payment, if it's a new order call the create-order…
Jun 22, 2024
React Native Pitfalls and Fixes
Publishing to Android App Stores Rejected for Privacy Violations Some Chinese app stores will reject your app with a message like "SDK reads private user…
Jun 3, 2024
Listing and Switching Input Methods on Windows
Approach 1: Via Keyboard Layout Limitation Extracting the layout ID is non-trivial — the code above uses a simplified low-word extraction. For a thorough…
Apr 20, 2024
Pre-rendering React Apps with Webpack
Background For various reasons — performance, SEO, accessibility — it's desirable for React (or other virtual DOM) apps to serve a static version of the page…
May 7, 2023
Handling CSS-in-JS Style Conflicts
A component library built with was repeatedly getting its styles overridden when integrated into a project that used multiple tech stacks. As you can see in…
Apr 22, 2023
Inside Webpack Loaders and Rules
The Problem A webpack-based React project worked fine on macOS and Linux but threw an error on Windows when processing SCSS: Re-installing and confirmed they…
Sep 29, 2022
Why Doesn't Listening on the Same Port in Multiple Node.js Cluster Workers Throw EADDRINUSE?
The Node.js docs show this pattern without any explanation of why it works: Everyone knows that listening on a port twice throws: And indeed, without : So how…
Sep 26, 2022
Inside Webpack Loaders and Rules

Inside Webpack Loaders and Rules

September 29, 2022

The Problem

A webpack-based React project worked fine on macOS and Linux but threw an error on Windows when processing SCSS:

ERROR in ./src/styles.scss 1:0
Module parse failed: Unexpected character '@' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> @import 'mixins.scss';

Re-installing sass and sass-loader confirmed they were present:

❯ npm ls sass sass-loader webpack
the-project@ path-of-the-project
+-- sass-loader@8.0.2
| `-- sass@1.32.13 deduped
`-- sass@1.32.13

Suspecting a rule.test matching issue, I looked at the SCSS rule:

{
  test: /\/((src\/(patha|pathb|pathc))|(node_modules))\/(.*)\.(sa|sc|c)ss$/,
  use: ["css-loader", "postcss-loader", "sass-loader"],
};

Simplifying the regex fixed it:

{
  test: /(.*)\.(sa|sc|c)ss$/,
  use: ["css-loader", "postcss-loader", "sass-loader"],
};

Root cause: Windows uses \ as the path separator while Unix uses /. The original regex only matched /-separated paths, so Windows paths silently fell through with no matching loader.

How webpack Loaders Work

Starting from the error message — it lives in node_modules/webpack/lib/ModuleParseError.js. Setting a breakpoint and walking the call stack leads to NormalModule.js:doBuild.

Module Parse Error

Call Stack

The exception is thrown inside NormalModule.js:doBuild.

Parser

A conditional breakpoint reveals the parser detail:

Conditional Breakpoint

Parser Exception

The exception originates in node_modules/webpack/lib/Parser.js:parse. acorn is a JavaScript parser — passing SCSS to a JavaScript parser will always fail. The correct pipeline is: sass-loader compiles SCSS → CSS, css-loader converts CSS → CommonJS, then either style-loader injects it into the page or mini-css-extract-plugin extracts it to a separate file.

Style Loader

In the working case, style-loader generates glue code that gets parsed by acorn as valid JavaScript.

Back in node_modules/loader-runner/lib/LoaderRunner.js:iterateNormalLoaders, loaderContext.loaders should be populated from the matched rules — but when no rule matches, no loaders run, and raw SCSS reaches the parser.

How webpack Rules Matching Works

Following the call stack from NormalModuleFactory.js:

this.ruleSet = new RuleSet(options.defaultRules.concat(options.rules));

Call Stack

Rules

RuleSet (in node_modules/webpack/lib/RuleSet.js) initialises by converting every test field to a matching function:

// node_modules/webpack/lib/RuleSet.js:normalizeCondition
 
if (typeof condition === "string") {
  return (str) => str.indexOf(condition) === 0;
}
if (typeof condition === "function") {
  return condition;
}
if (condition instanceof RegExp) {
  return condition.test.bind(condition);
}
if (Array.isArray(condition)) {
  const items = condition.map((c) => RuleSet.normalizeCondition(c));
  return orMatcher(items);
}

Matching happens in RuleSet.js:exec, which runs those functions against the file path.

Ruleset Execution

The separator difference (/ vs \) means the original regex never matched Windows paths — so no loaders were applied, and SCSS was handed raw to the JavaScript parser.

#Webpack#Loaders#Windows