Skip to content

[heft-sass-plugin] Add preserveIcssExports option#5762

Merged
iclanton merged 10 commits intomicrosoft:mainfrom
iclanton:heft-sass-plugin/preserve-icss-exports
Apr 9, 2026
Merged

[heft-sass-plugin] Add preserveIcssExports option#5762
iclanton merged 10 commits intomicrosoft:mainfrom
iclanton:heft-sass-plugin/preserve-icss-exports

Conversation

@iclanton
Copy link
Copy Markdown
Member

@iclanton iclanton commented Apr 9, 2026

Summary

When cssOutputFolders is configured to emit CSS files for consumption by a webpack loader, the ICSS :export block was being stripped from the output. This broke downstream loaders such as css-loader's icssParser that depend on the :export block being present in the CSS file to generate JavaScript module exports at bundle time.

A new preserveIcssExports boolean option (default false) is added to sass.json. When set to true, the CSS is written to disk unchanged so that the :export block is available for downstream processing.

Details

postcss-modules is run on CSS module files to extract class names and :export values for .d.ts generation. As a side effect it strips the :export block from the CSS output — this is correct behavior in a traditional CSS Modules pipeline where css-loader consumes the CSS directly and generates JS inline, but is incorrect when heft-sass-plugin emits CSS files to disk for a separate webpack pass to consume.

The fix runs postcss-modules purely for its side effect of populating the module map via the getJSON callback, then conditionally discards the transformed CSS in favor of the original when preserveIcssExports: true. The :export block is the only thing postcss-modules removes from the CSS for standard compiled SCSS (class name scoping with the identity generateScopedName is a no-op round-trip).

The option defaults to false to preserve backwards compatibility.

How it was tested

Verified by building a project with .module.scss files containing only an :export block and confirming the emitted .css file contains the block, allowing the downstream webpack bundle to correctly export the values as a JavaScript default export.

@github-project-automation github-project-automation bot moved this to Needs triage in Bug Triage Apr 9, 2026
@iclanton iclanton force-pushed the heft-sass-plugin/preserve-icss-exports branch from daa8bde to 0daa4e6 Compare April 9, 2026 04:16
@iclanton iclanton enabled auto-merge (squash) April 9, 2026 04:16
Copy link
Copy Markdown
Contributor

@dmichon-msft dmichon-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally not a fan of file system crawls to find files at static known locations. Could also probably read this from an environment variable? We should consider making that be part of the environment for heft-jest-plugin if it isn't already.

iclanton and others added 7 commits April 9, 2026 12:57
Add a new `preserveIcssExports` option to `sass.json` that preserves the ICSS `:export` block in the emitted CSS output. When false (the default), `postcss-modules` strips `:export` from the CSS as before. When true, the CSS is left unchanged so that downstream webpack loaders (e.g. css-loader's icssParser) can extract the `:export` values at bundle time to generate JavaScript module exports.
Tests cover CSS output (preserveIcssExports true/false), .d.ts generation, Sass-specific features (variables + nesting, @mixin, @extend + placeholders), and error reporting for invalid SCSS.
Tests cover CSS output (preserveIcssExports true/false), .d.ts generation, Sass-specific features (variables + nesting, @mixin, @extend + placeholders), and error reporting for invalid SCSS.
Tests cover CSS output (preserveIcssExports true/false), .d.ts generation,
Sass-specific features (variables + nesting, @mixin, @extend + placeholders),
JS shim generation (commonjs/esnext, module/global), multiple output folders,
postProcessCssAsync callback, exportAsDefault: false, and error reporting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…option

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@iclanton iclanton force-pushed the heft-sass-plugin/preserve-icss-exports branch from 8bbc7f6 to 89665b5 Compare April 9, 2026 20:15
iclanton and others added 3 commits April 9, 2026 13:42
On Windows, path.resolve('/fake/output/css', ...) prepends the drive
letter and uses backslashes. Normalize paths in the FileSystem mock to
forward slashes with no drive letter so that map lookups and snapshots
are consistent across platforms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@iclanton iclanton merged commit de3c859 into microsoft:main Apr 9, 2026
6 checks passed
@github-project-automation github-project-automation bot moved this from Needs triage to Closed in Bug Triage Apr 9, 2026
@iclanton iclanton deleted the heft-sass-plugin/preserve-icss-exports branch April 9, 2026 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Closed

Development

Successfully merging this pull request may close these issues.

2 participants