Skip to content

Commit de3c859

Browse files
iclantonclaude
andauthored
[heft-sass-plugin] Add preserveIcssExports option (#5762)
* [heft-sass-plugin] Add preserveIcssExports option 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. * fixup! [heft-sass-plugin] Add preserveIcssExports option * Add snapshot tests for SassProcessor Tests cover CSS output (preserveIcssExports true/false), .d.ts generation, Sass-specific features (variables + nesting, @mixin, @extend + placeholders), and error reporting for invalid SCSS. * Add snapshot tests for SassProcessor Tests cover CSS output (preserveIcssExports true/false), .d.ts generation, Sass-specific features (variables + nesting, @mixin, @extend + placeholders), and error reporting for invalid SCSS. * Add snapshot tests for SassProcessor 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> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a9c69b6 commit de3c859

16 files changed

+1652
-3
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/heft-sass-plugin",
5+
"comment": "Add `preserveIcssExports` option to keep the ICSS `:export` block in emitted CSS output, required when downstream webpack loaders (e.g. `css-loader` icssParser) need to extract `:export` values at bundle time.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/heft-sass-plugin"
10+
}

common/config/subspaces/default/pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "local-node-rig/profiles/default/config/jest.config.json"
3+
}

heft-plugins/heft-sass-plugin/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"devDependencies": {
5757
"@microsoft/api-extractor": "workspace:*",
5858
"@rushstack/heft": "workspace:*",
59+
"@rushstack/terminal": "workspace:*",
5960
"eslint": "~9.37.0",
6061
"local-node-rig": "workspace:*"
6162
},

heft-plugins/heft-sass-plugin/src/SassPlugin.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface ISassConfigurationJson {
3030
silenceDeprecations?: string[];
3131
excludeFiles?: string[];
3232
doNotTrimOriginalFileExtension?: boolean;
33+
preserveIcssExports?: boolean;
3334
}
3435

3536
const SASS_CONFIGURATION_LOCATION: string = 'config/sass.json';
@@ -98,7 +99,8 @@ export default class SassPlugin implements IHeftPlugin {
9899
nonModuleFileExtensions,
99100
silenceDeprecations,
100101
excludeFiles,
101-
doNotTrimOriginalFileExtension
102+
doNotTrimOriginalFileExtension,
103+
preserveIcssExports
102104
} = sassConfigurationJson || {};
103105

104106
function resolveFolder(folder: string): string {
@@ -126,6 +128,7 @@ export default class SassPlugin implements IHeftPlugin {
126128
}),
127129
silenceDeprecations,
128130
doNotTrimOriginalFileExtension,
131+
preserveIcssExports,
129132
postProcessCssAsync: hooks.postProcessCss.isUsed()
130133
? async (cssText: string) => hooks.postProcessCss.promise(cssText)
131134
: undefined

heft-plugins/heft-sass-plugin/src/SassProcessor.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ export interface ISassProcessorOptions {
121121
*/
122122
doNotTrimOriginalFileExtension?: boolean;
123123

124+
/**
125+
* If true, the ICSS `:export` block will be preserved in the emitted CSS output. This is necessary
126+
* when the CSS is consumed by a webpack loader (e.g. css-loader's icssParser) that extracts `:export`
127+
* values at bundle time to generate JavaScript exports.
128+
*
129+
* Defaults to false.
130+
*/
131+
preserveIcssExports?: boolean;
132+
124133
/**
125134
* A callback to further modify the raw CSS text after it has been generated. Only relevant if emitting CSS files.
126135
*/
@@ -751,7 +760,8 @@ export class SassProcessor {
751760
srcFolder,
752761
exportAsDefault,
753762
doNotTrimOriginalFileExtension,
754-
postProcessCssAsync
763+
postProcessCssAsync,
764+
preserveIcssExports
755765
} = this._options;
756766

757767
// Handle CSS modules
@@ -769,7 +779,14 @@ export class SassProcessor {
769779
const postCssResult: postcss.Result = await postcss
770780
.default([postCssModules])
771781
.process(css, { from: sourceFilePath });
772-
css = postCssResult.css;
782+
783+
if (!preserveIcssExports) {
784+
// Default behavior: use the transformed CSS output, which has the :export block stripped.
785+
css = postCssResult.css;
786+
}
787+
// If preserveIcssExports is true, we discard the transformed CSS and keep the original so
788+
// that the :export block remains in the output for downstream webpack loaders (e.g.
789+
// css-loader's icssParser) that extract :export values at bundle time.
773790
}
774791

775792
if (postProcessCssAsync) {

heft-plugins/heft-sass-plugin/src/schemas/heft-sass-plugin.schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@
111111
"doNotTrimOriginalFileExtension": {
112112
"type": "boolean",
113113
"description": "If true, the original file extension will not be trimmed when generating the output CSS file. The generated CSS file will retain its original extension. For example, \"styles.scss\" will generate \"styles.scss.css\" instead of \"styles.css\"."
114+
},
115+
116+
"preserveIcssExports": {
117+
"type": "boolean",
118+
"description": "If true, the ICSS `:export` block will be preserved in the emitted CSS output. This is necessary when the CSS is consumed by a webpack loader (e.g. css-loader's icssParser) that extracts `:export` values at bundle time to generate JavaScript exports. Defaults to false."
114119
}
115120
}
116121
}

0 commit comments

Comments
 (0)