Skip to content

Commit 6c7a323

Browse files
committed
Add CSS module capabilities to SSR
1 parent 99c08ce commit 6c7a323

File tree

4 files changed

+94
-51
lines changed

4 files changed

+94
-51
lines changed

client/webpack.common.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
const { cpus } = require( 'os' );
2+
const path = require( 'path' );
3+
const SassConfig = require( '@automattic/calypso-build/webpack/sass' );
4+
const { cssNameFromFilename } = require( '@automattic/calypso-build/webpack/util' );
5+
const autoprefixerPlugin = require( 'autoprefixer' );
26

37
/**
48
* Get an env var that should be a positive integer greater than 0
@@ -35,6 +39,61 @@ if ( concurrentBuilds > 1 ) {
3539
workerCount = Math.max( 1, Math.floor( workerCount / concurrentBuilds ) );
3640
}
3741

42+
const getOutputFileName = ( { isDevelopment } ) => {
43+
let outputFilename = '[name].[contenthash].min.js';
44+
let outputChunkFilename = '[name].[contenthash].min.js';
45+
46+
// we should not use chunkhash in development: https://github.com/webpack/webpack-dev-server/issues/377#issuecomment-241258405
47+
// also we don't minify so dont name them .min.js
48+
if ( isDevelopment ) {
49+
outputFilename = '[name].js';
50+
outputChunkFilename = '[name].js';
51+
}
52+
53+
return {
54+
outputFilename,
55+
outputChunkFilename,
56+
};
57+
};
58+
59+
const getSCSSConfig = ( { outputFilename, outputChunkFilename } ) => {
60+
const cssFilename = cssNameFromFilename( outputFilename );
61+
const cssChunkFilename = cssNameFromFilename( outputChunkFilename );
62+
63+
const loader = SassConfig.loader( {
64+
includePaths: [ __dirname ],
65+
postCssOptions: {
66+
// Do not use postcss.config.js. This ensure we have the final say on how PostCSS is used in calypso.
67+
// This is required because Calypso imports `@automattic/notifications` and that package defines its
68+
// own `postcss.config.js` that they use for their webpack bundling process.
69+
config: false,
70+
plugins: [ autoprefixerPlugin() ],
71+
},
72+
// Since `prelude` string will be appended to each Sass file
73+
// We need to ensure that the import path (inside a sass file) is a posix path, regardless of the OS/platform
74+
// Final result should be something like `@use 'client/assets/stylesheets/shared/_utils.scss' as *;`
75+
prelude: `@use '${
76+
path
77+
// Path, relative to Node CWD
78+
.relative( process.cwd(), path.join( __dirname, 'assets/stylesheets/shared/_utils.scss' ) )
79+
.split( path.sep ) // Break any path (posix/win32) by path separator
80+
.join( path.posix.sep ) // Convert the path explicitly to posix to ensure imports work fine
81+
}' as *;`,
82+
} );
83+
84+
const plugins = SassConfig.plugins( {
85+
chunkFilename: cssChunkFilename,
86+
filename: cssFilename,
87+
} );
88+
89+
return {
90+
loader,
91+
plugins,
92+
};
93+
};
94+
3895
module.exports = {
3996
workerCount,
97+
getOutputFileName,
98+
getSCSSConfig,
4099
};

client/webpack.config.js

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,12 @@
55
const path = require( 'path' );
66
const FileConfig = require( '@automattic/calypso-build/webpack/file-loader' );
77
const Minify = require( '@automattic/calypso-build/webpack/minify' );
8-
const SassConfig = require( '@automattic/calypso-build/webpack/sass' );
98
const TranspileConfig = require( '@automattic/calypso-build/webpack/transpile' );
10-
const {
11-
cssNameFromFilename,
12-
shouldTranspileDependency,
13-
} = require( '@automattic/calypso-build/webpack/util' );
9+
const { shouldTranspileDependency } = require( '@automattic/calypso-build/webpack/util' );
1410
const ExtensiveLodashReplacementPlugin = require( '@automattic/webpack-extensive-lodash-replacement-plugin' );
1511
const InlineConstantExportsPlugin = require( '@automattic/webpack-inline-constant-exports-plugin' );
1612
const ReactRefreshWebpackPlugin = require( '@pmmmwh/react-refresh-webpack-plugin' );
1713
const SentryCliPlugin = require( '@sentry/webpack-plugin' );
18-
const autoprefixerPlugin = require( 'autoprefixer' );
1914
const CircularDependencyPlugin = require( 'circular-dependency-plugin' );
2015
const Dotenv = require( 'dotenv-webpack' );
2116
const DuplicatePackageCheckerPlugin = require( 'duplicate-package-checker-webpack-plugin' );
@@ -29,7 +24,7 @@ const GenerateChunksMapPlugin = require( '../build-tools/webpack/generate-chunks
2924
const ReadOnlyCachePlugin = require( '../build-tools/webpack/readonly-cache-plugin' );
3025
const RequireChunkCallbackPlugin = require( '../build-tools/webpack/require-chunk-callback-plugin' );
3126
const config = require( './server/config' );
32-
const { workerCount } = require( './webpack.common' );
27+
const { workerCount, getSCSSConfig, getOutputFileName } = require( './webpack.common' );
3328
/**
3429
* Internal variables
3530
*/
@@ -140,18 +135,8 @@ if ( ! process.env.BROWSERSLIST_ENV ) {
140135
process.env.BROWSERSLIST_ENV = browserslistEnv;
141136
}
142137

143-
let outputFilename = '[name].[contenthash].min.js';
144-
let outputChunkFilename = '[name].[contenthash].min.js';
145-
146-
// we should not use chunkhash in development: https://github.com/webpack/webpack-dev-server/issues/377#issuecomment-241258405
147-
// also we don't minify so dont name them .min.js
148-
if ( isDevelopment ) {
149-
outputFilename = '[name].js';
150-
outputChunkFilename = '[name].js';
151-
}
152-
153-
const cssFilename = cssNameFromFilename( outputFilename );
154-
const cssChunkFilename = cssNameFromFilename( outputChunkFilename );
138+
const { outputFilename, outputChunkFilename } = getOutputFileName( { isDevelopment } );
139+
const scssConfig = getSCSSConfig( { outputFilename, outputChunkFilename } );
155140

156141
const outputDir = path.resolve( '.' );
157142

@@ -235,29 +220,7 @@ const webpackConfig = {
235220
cacheCompression: false,
236221
include: shouldTranspileDependency,
237222
} ),
238-
SassConfig.loader( {
239-
includePaths: [ __dirname ],
240-
postCssOptions: {
241-
// Do not use postcss.config.js. This ensure we have the final say on how PostCSS is used in calypso.
242-
// This is required because Calypso imports `@automattic/notifications` and that package defines its
243-
// own `postcss.config.js` that they use for their webpack bundling process.
244-
config: false,
245-
plugins: [ autoprefixerPlugin() ],
246-
},
247-
// Since `prelude` string will be appended to each Sass file
248-
// We need to ensure that the import path (inside a sass file) is a posix path, regardless of the OS/platform
249-
// Final result should be something like `@use 'client/assets/stylesheets/shared/_utils.scss' as *;`
250-
prelude: `@use '${
251-
path
252-
// Path, relative to Node CWD
253-
.relative(
254-
process.cwd(),
255-
path.join( __dirname, 'assets/stylesheets/shared/_utils.scss' )
256-
)
257-
.split( path.sep ) // Break any path (posix/win32) by path separator
258-
.join( path.posix.sep ) // Convert the path explicitly to posix to ensure imports work fine
259-
}' as *;`,
260-
} ),
223+
scssConfig.loader,
261224
{
262225
include: path.join( __dirname, 'sections.js' ),
263226
loader: path.join( __dirname, '../build-tools/webpack/sections-loader' ),
@@ -320,10 +283,7 @@ const webpackConfig = {
320283
} ),
321284
new webpack.NormalModuleReplacementPlugin( /^path$/, 'path-browserify' ),
322285
new webpack.IgnorePlugin( { resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ } ),
323-
...SassConfig.plugins( {
324-
chunkFilename: cssChunkFilename,
325-
filename: cssFilename,
326-
} ),
286+
...scssConfig.plugins,
327287
new AssetsWriter( {
328288
filename: `assets.json`,
329289
path: path.join( outputDir, 'build' ),

client/webpack.config.node.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const { packagesInMonorepo } = require( '../build-tools/lib/monorepo' );
1414
const ExternalModulesWriter = require( './server/bundler/external-modules' );
1515
const config = require( './server/config' );
1616
const bundleEnv = config( 'env' );
17-
const { workerCount } = require( './webpack.common' );
17+
const { workerCount, getSCSSConfig, getOutputFileName } = require( './webpack.common' );
1818

1919
/**
2020
* Internal variables
@@ -81,6 +81,9 @@ function getExternals() {
8181

8282
const buildDir = path.resolve( 'build' );
8383

84+
const { outputFilename, outputChunkFilename } = getOutputFileName( { isDevelopment } );
85+
const scssConfig = getSCSSConfig( { outputFilename, outputChunkFilename } );
86+
8487
const webpackConfig = {
8588
devtool: 'source-map',
8689
entry: path.join( __dirname, 'server' ),
@@ -127,10 +130,7 @@ const webpackConfig = {
127130
include: shouldTranspileDependency,
128131
} ),
129132
fileLoader,
130-
{
131-
test: /\.(sc|sa|c)ss$/,
132-
loader: 'ignore-loader',
133-
},
133+
scssConfig.loader,
134134
],
135135
},
136136
resolve: {
@@ -169,6 +169,7 @@ const webpackConfig = {
169169
'process.env.NODE_ENV': JSON.stringify( bundleEnv ),
170170
__i18n_text_domain__: JSON.stringify( 'default' ),
171171
} ),
172+
...scssConfig.plugins,
172173
new webpack.IgnorePlugin( { resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ } ),
173174
! isDevelopment && new ExternalModulesWriter(),
174175
].filter( Boolean ),

packages/calypso-build/webpack/sass.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,29 @@ module.exports.loader = ( { includePaths, prelude, postCssOptions } ) => ( {
1818
loader: require.resolve( 'css-loader' ),
1919
options: {
2020
importLoaders: 2,
21+
modules: {
22+
auto: /\.module\.scss$/, // Only enable CSS modules for .module.scss files
23+
getLocalIdent: ( context, localIdentName, localName ) => {
24+
// Only apply custom naming for .module.scss files
25+
if ( ! /\.module\.scss$/.test( context.resourcePath ) ) {
26+
// For non-modules, return the original class name (no hashing)
27+
return localName;
28+
}
29+
30+
// Custom class name for modules
31+
return (
32+
context.resourcePath.split( '/' ).slice( -2 ).join( '_' ) +
33+
'__' +
34+
localName +
35+
'___' +
36+
require( 'crypto' )
37+
.createHash( 'md5' )
38+
.update( context.resourcePath + localName )
39+
.digest( 'hex' )
40+
.substr( 0, 5 )
41+
);
42+
},
43+
},
2144
// We do not want css-loader to resolve absolute paths. We
2245
// typically use `/` to indicate the start of the base URL,
2346
// but starting with css-loader v4, it started trying to handle

0 commit comments

Comments
 (0)