Skip to content

[SPFx 1.23] CSS/SCSS source maps not working in development mode — regression from SPFx 1.20 #10831

@StfBauer

Description

@StfBauer

Target SharePoint environment: SharePoint Online
Framework: SharePoint Framework

Additional environment details:

  • SPFx version: 1.23.0 (GA)
  • Node.js version: >= 22.14.0
  • Developer OS: macOS / Windows
  • @microsoft/spfx-heft-plugins: 1.23.0
  • @microsoft/sp-css-loader: 1.23.0
  • @rushstack/heft-sass-plugin: 1.3.8

Describe the bug / error

CSS and SCSS source maps are not functional in development (heft start) mode in SPFx 1.23.0 (GA).
This is a continuation of the regression introduced with the Heft toolchain migration in SPFx 1.22.
TypeScript/TSX source maps still work. This regression is CSS/SCSS-only.

The SPFx team explicitly committed to fixing this in the 1.23 release (see #10490), but the GA
ships with @rushstack/heft-sass-plugin at the same version (1.3.8) as the RC, with no changes
to the source map chain.

Related:


Steps to reproduce

  1. Scaffold a new SPFx 1.23.0 web part project
  2. Run heft start --clean
  3. Open the workbench in a browser and open DevTools
  4. Inspect any CSS rule applied by your web part
  5. Click the source link in the Styles panel

Expected behavior

When running heft start, the source link in DevTools should trace back to the original
.scss source file:

src/webparts/myWebPart/components/MyWebPart.module.scss  <- expected

This worked in SPFx 1.20-1.21 (Gulp toolchain).


Actual behaviour

Source maps for CSS/SCSS point to the intermediate compiled .css file in lib/, not the
original .scss source. In the generated dist/one-22-web-part.js.map:

"sources": [
  "webpack:///.././lib/webparts/one22/components/One22.module.scss.css",
  ...
]
  • No .scss paths appear in the bundle source map.
  • No .map files are generated in lib/ alongside the compiled CSS.
  • sourcesContent embeds the compiled intermediate JS module, not the original SCSS.

Root Cause Analysis

The Heft-based build chain has three independent points where the source map chain is broken.

Point 1 — @rushstack/heft-sass-plugin (v1.3.8): No .map output

The Sass compilation step outputs compiled CSS to lib/ but generates no .map files.
The SassProcessor does not pass source map options to sass-embedded, and no sourceMap
option is exposed in the plugin's configuration schema or ISassProcessorOptions interface.

One22.module.scss
  -> [heft-sass-plugin]
  -> lib/webparts/one22/components/One22.module.scss.css   <- no .map generated

Point 2 — @microsoft/sp-css-loader (v1.23.0): No source map support

sp-css-loader is an independent reimplementation — not a wrapper around css-loader. It
runs PostCSS directly but passes no map option to processor.process():

sp-css-loader/lib-commonjs/index.js ~line 87:

result = await processor.process(content, {
    from: resourcePath,
    to: resourcePath
    // No map option
});

The webpack loader chain is also built without a sourceMap option:

WebpackConfigurationGenerator.js ~line 264:

const simpleCssLoaderOptions = {
    async: true,
    loadThemedStylesImportPath: options.loadThemedStylesImportPath,
    production: options.production
    // No sourceMap option
};

Compare to how sourceMap is correctly forwarded to ModuleMinifierPlugin for JS (~line 519):

sourceMap: !!devtool   // exists for JS minifier, absent for CSS loader

Point 3 — source-map-loader only processes .js files

Even in dev mode where devtool: 'source-map' is set, the source-map-loader is registered
only for .js files:

WebpackConfigurationGenerator.js ~line 528:

config.module.rules.push({
    test: /\.js$/,     // JS only; CSS never processed
    enforce: 'pre',
    use: { loader: 'source-map-loader', ... }
});

CSS files are never processed by source-map-loader. And since Point 1 produces no .map
files anyway, there is nothing to pick up even if the rule were widened.

Resulting chain vs. TypeScript (comparison)

CSS/SCSS — broken:

One22.module.scss
  -> [heft-sass-plugin, no map output]
  -> lib/One22.module.scss.css   (no .map)
  -> [sp-css-loader, no sourceMap option]
  -> webpack bundle
  -> dist/bundle.js.map
      sources: ["webpack:///../lib/.../One22.module.scss.css"]  <- points to lib/, not src/

TypeScript — working:

One22WebPart.ts
  -> [tsc, sourceMap: true + inlineSources: true]
  -> lib/One22WebPart.js + One22WebPart.js.map  (-> src/)
  -> [source-map-loader, resolves chain]
  -> dist/bundle.js.map
      sources: ["webpack:///../src/.../One22WebPart.ts"]  <- correct!
      sourcesContent: [embedded TypeScript source]

History

SPFx Version Toolchain CSS Source Maps
<= 1.18 Gulp Not working
1.19.0 Gulp Partial / unreliable
1.20.x - 1.21.x Gulp Working
1.22.x (beta -> GA) Heft Broken (regression)
1.23.0 (GA) Heft Still broken — commitment missed

Reference: Improved CSS debugging in SharePoint 1.20
documents the working state and why CSS source maps matter for debugging complex SPFx solutions.


Missed Commitment

During the SPFx 1.22.0-rc.0 release cycle, the SPFx team explicitly committed to delivering
SASS source map support in the 1.23 release. From the opening post of discussion #10490:

"SASS Sourcemaps are not currently supported by Heft SASS plugin at this time. We will work
to get this added to Heft, and it will be included in 1.23 release."
@nick-pape, 2025-11-19

SPFx 1.23.0 GA has shipped and — as the technical analysis above confirms — this commitment
has not been met. @rushstack/heft-sass-plugin ships at the same version (1.3.8) as it did
during the RC cycle. The three broken points in the chain remain unchanged.


Fix Required

Restoring CSS/SCSS source maps requires addressing all three points in the chain:

  1. @rushstack/heft-sass-plugin: Add sourceMap option and emit .map files alongside
    compiled CSS output in cssOutputFolders.

  2. @microsoft/sp-css-loader: Expose and forward a sourceMap option to the PostCSS
    pipeline (map option on processor.process()).

  3. @microsoft/spfx-heft-plugins webpack config: Pass sourceMap: !!devtool to the CSS
    loader options in WebpackConfigurationGenerator.js (mirroring what already exists for
    ModuleMinifierPlugin).

Metadata

Metadata

Assignees

Labels

Needs: Triage 🔍Awaiting categorization and initial review.area:spfxCategory: SharePoint Framework (not extensions related)area:toolingCategory: Development toolingtype:bug-confirmedConfirmed bug, not working as designed / expected.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions