Skip to content

Commit 366eeb7

Browse files
Refactor: Refresh the CSS in dev mode
Fix: #1792 - Added `style-loader` version 4.0.0 to the project dependencies in `package.json`. - Updated `webpack.config.js` to use `style-loader` for injecting CSS in development mode, allowing SCSS changes to apply after reload without a separate .css file. - Made minor formatting adjustments for better readability in the configuration file. - Explain the Regex so we can understand that in a few years.
1 parent 8d7f411 commit 366eeb7

File tree

3 files changed

+101
-39
lines changed

3 files changed

+101
-39
lines changed

packages/transition-frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
"rimraf": "^6.1.2",
9696
"sass": "^1.94.2",
9797
"sass-loader": "^16.0.6",
98+
"style-loader": "^4.0.0",
9899
"ts-jest": "^29.4.6",
99100
"ts-loader": "^9.5.4",
100101
"ts-shader-loader": "^2.0.2",

packages/transition-frontend/webpack.config.js

Lines changed: 86 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ const configuration = require('chaire-lib-backend/lib/config/server.config');
2525
// Extract from the config all options that we should not send to the frontend.
2626
// The `{ ...config }` will be sent to the frontend
2727
// TODO This won't be necessary once we have frontend and backend configuration separated
28-
const { trRoutingCacheAllScenarios, routing, ...config } = configuration.default ? configuration.default : configuration;
28+
const { trRoutingCacheAllScenarios, routing, ...config } = configuration.default
29+
? configuration.default
30+
: configuration;
2931

3032
// Public directory from which files are served
3133
const publicDirectory = path.join(__dirname, '..', '..', 'public');
@@ -41,18 +43,29 @@ module.exports = (env) => {
4143
const isProduction = process.env.NODE_ENV === 'production';
4244

4345
// Make sure all builds have different names if not a production bundle
44-
const bundleFileName = isProduction ? `transition-${config.projectShortname}-bundle-${process.env.NODE_ENV}.[contenthash].js` : `transition-${config.projectShortname}-bundle-${process.env.NODE_ENV}.dev.js`;
45-
const styleFileName = isProduction ? `transition-${config.projectShortname}-styles.[contenthash].css` : `transition-${config.projectShortname}-styles.dev.css`;
46+
const bundleFileName = isProduction
47+
? `transition-${config.projectShortname}-bundle-${process.env.NODE_ENV}.[contenthash].js`
48+
: `transition-${config.projectShortname}-bundle-${process.env.NODE_ENV}.dev.js`;
49+
const styleFileName = isProduction
50+
? `transition-${config.projectShortname}-styles.[contenthash].css`
51+
: `transition-${config.projectShortname}-styles.dev.css`;
4652
// HTML main file name
4753
const htmlFileName = path.join(`index-${config.projectShortname}.html`);
4854

55+
// In dev, style-loader injects CSS via <style> so SCSS changes apply after reload without a separate .css file
56+
const styleLoader = isProduction ? MiniCssExtractPlugin.loader : 'style-loader';
57+
const chaireLibFrontendRoot = path.dirname(require.resolve('chaire-lib-frontend/package.json'));
58+
const transitionFrontendRoot = __dirname;
59+
4960
const languages = config.languages || ['fr', 'en'];
5061
const languagesFilter = `/${languages.join('|')}/`;
5162

5263
// TODO Custom styles and locales should be set in config (#419, #420)
5364
const customStylesFilePath = `${config.projectDir}/styles/styles.scss`;
5465
const customLocalesFilePath = `${config.projectDir}/locales`;
55-
const entry = fs.existsSync('./' + customStylesFilePath) ? [entryFileName, './' + customStylesFilePath] : [entryFileName];
66+
const entry = fs.existsSync('./' + customStylesFilePath)
67+
? [entryFileName, './' + customStylesFilePath]
68+
: [entryFileName];
5669
const includeDirectories = [
5770
path.join(__dirname, 'lib'),
5871

@@ -64,7 +77,7 @@ module.exports = (env) => {
6477
// Controls which information to display (see https://webpack.js.org/configuration/stats/)
6578
stats: {
6679
errorDetails: true,
67-
children: true,
80+
children: true
6881
},
6982
node: {
7083
// global will be deprecated at next major release, see where it is being used
@@ -78,15 +91,34 @@ module.exports = (env) => {
7891
publicPath: '/dist/'
7992
},
8093
watchOptions: {
81-
ignored: ['node_modules/**'],
94+
// In dev, watch chaire-lib-frontend so CSS/TS changes trigger rebuild
95+
// Exclude lib/styles from chaire-lib-frontend and transition-frontend since we use alias to point to src/styles
96+
ignored: isProduction
97+
? // In production, ignore all node_modules to avoid rebuilding the whole project
98+
new RegExp('node_modules/')
99+
: /* Ignore all node_modules except chaire-lib-frontend,
100+
and also specifically ignore the lib/styles directory of chaire-lib-frontend and transition-frontend
101+
(which we override via an alias to src/styles).
102+
Paths are normalized and escaped: .replace(/\\/g,'/') for forward slashes and
103+
.replace(/[.*+?^${}()|[\]\\]/g,'\\$&') escapes regex metacharacters (. * + ? ^ $ { } ( ) | [ ] \)
104+
so the path is matched literally (e.g. paths with parentheses like "Program Files (x86)"). */
105+
new RegExp(
106+
`(node_modules\\/(?!chaire-lib-frontend)|${path
107+
.join(chaireLibFrontendRoot, 'lib', 'styles')
108+
.replace(/\\/g, '/')
109+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}|${path
110+
.join(transitionFrontendRoot, 'lib', 'styles')
111+
.replace(/\\/g, '/')
112+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`
113+
),
82114
aggregateTimeout: 600
83115
},
84116
module: {
85117
rules: [
86118
{
87119
test: /\.tsx?$/,
88120
use: 'ts-loader',
89-
exclude: /node_modules/,
121+
exclude: /node_modules/
90122
},
91123
{
92124
use: 'json-loader',
@@ -104,7 +136,7 @@ module.exports = (env) => {
104136
{
105137
test: /\.s?css$/,
106138
use: [
107-
MiniCssExtractPlugin.loader,
139+
styleLoader, // In dev, style-loader injects CSS via <style> so SCSS changes apply after reload without a separate .css file
108140
{
109141
loader: 'css-loader',
110142
options: {
@@ -124,7 +156,7 @@ module.exports = (env) => {
124156
loader: '@alienfast/i18next-loader',
125157
options: {
126158
basenameAsNamespace: true,
127-
overrides: (fs.existsSync('./' + customLocalesFilePath) ? ['../' + customLocalesFilePath] : [])
159+
overrides: fs.existsSync('./' + customLocalesFilePath) ? ['../' + customLocalesFilePath] : []
128160
}
129161
}
130162
]
@@ -133,32 +165,38 @@ module.exports = (env) => {
133165
new CleanWebpackPlugin({
134166
dry: !isProduction,
135167
verbose: true,
136-
cleanAfterEveryBuildPatterns: ['**/*', '!images/**', '!*.html'],
168+
cleanAfterEveryBuildPatterns: ['**/*', '!images/**', '!*.html']
137169
}),
138170
new HtmlWebpackPlugin({
139171
filename: htmlFileName,
140-
template: path.join(publicDirectory, 'index.html'),
172+
template: path.join(publicDirectory, 'index.html')
141173
}),
142174
new MiniCssExtractPlugin({
143175
filename: styleFileName
144176
}),
145177
new webpack.DefinePlugin({
146178
'process.env': {
147-
'IS_BROWSER': JSON.stringify(true),
148-
'HOST': JSON.stringify(process.env.HOST),
149-
'TRROUTING_HOST': JSON.stringify(process.env.TRROUTING_HOST),
150-
'PROJECT_SOURCE': JSON.stringify(process.env.PROJECT_SOURCE),
151-
'IS_TESTING': JSON.stringify(process.env.NODE_ENV === 'test'),
152-
'GOOGLE_API_KEY': JSON.stringify(process.env.GOOGLE_API_KEY),
153-
'CUSTOM_RASTER_TILES_XYZ_URL': JSON.stringify(process.env.CUSTOM_RASTER_TILES_XYZ_URL || config.customRasterTilesXyzUrl),
154-
'CUSTOM_RASTER_TILES_MIN_ZOOM': JSON.stringify(process.env.CUSTOM_RASTER_TILES_MIN_ZOOM || config.customRasterTilesMinZoom),
155-
'CUSTOM_RASTER_TILES_MAX_ZOOM': JSON.stringify(process.env.CUSTOM_RASTER_TILES_MAX_ZOOM || config.customRasterTilesMaxZoom)
179+
IS_BROWSER: JSON.stringify(true),
180+
HOST: JSON.stringify(process.env.HOST),
181+
TRROUTING_HOST: JSON.stringify(process.env.TRROUTING_HOST),
182+
PROJECT_SOURCE: JSON.stringify(process.env.PROJECT_SOURCE),
183+
IS_TESTING: JSON.stringify(process.env.NODE_ENV === 'test'),
184+
GOOGLE_API_KEY: JSON.stringify(process.env.GOOGLE_API_KEY),
185+
CUSTOM_RASTER_TILES_XYZ_URL: JSON.stringify(
186+
process.env.CUSTOM_RASTER_TILES_XYZ_URL || config.customRasterTilesXyzUrl
187+
),
188+
CUSTOM_RASTER_TILES_MIN_ZOOM: JSON.stringify(
189+
process.env.CUSTOM_RASTER_TILES_MIN_ZOOM || config.customRasterTilesMinZoom
190+
),
191+
CUSTOM_RASTER_TILES_MAX_ZOOM: JSON.stringify(
192+
process.env.CUSTOM_RASTER_TILES_MAX_ZOOM || config.customRasterTilesMaxZoom
193+
)
156194
},
157-
'__CONFIG__': JSON.stringify({
195+
__CONFIG__: JSON.stringify({
158196
...config
159197
})
160198
}),
161-
new webpack.optimize.AggressiveMergingPlugin(),//Merge chunks
199+
new webpack.optimize.AggressiveMergingPlugin(), //Merge chunks
162200
new CompressionPlugin({
163201
filename: '[path][base].gz[query]',
164202
algorithm: 'gzip',
@@ -168,24 +206,37 @@ module.exports = (env) => {
168206
}),
169207
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, new RegExp(languagesFilter)),
170208
new webpack.ContextReplacementPlugin(/date\-fns[\/\\]/, new RegExp(languagesFilter)),
171-
new CopyWebpackPlugin(
172-
{
173-
patterns: [
174-
{
175-
context: path.join(__dirname, 'lib', 'assets'),
176-
from: '**/*',
177-
to: '',
178-
noErrorOnMissing: true
179-
}
180-
]
181-
}
182-
)
209+
new CopyWebpackPlugin({
210+
patterns: [
211+
{
212+
context: path.join(__dirname, 'lib', 'assets'),
213+
from: '**/*',
214+
to: '',
215+
noErrorOnMissing: true
216+
}
217+
]
218+
})
183219
],
184220
resolve: {
185221
mainFields: ['browser', 'main', 'module'],
186222
modules: ['node_modules'],
187223
extensions: ['.json', '.js', '.ts', '.tsx'],
188-
fallback: { path: false },
224+
// In dev, read SCSS from chaire-lib-frontend and transition-frontend source so changes apply without running copy-files
225+
alias: isProduction
226+
? {}
227+
: {
228+
[path.join(chaireLibFrontendRoot, 'lib', 'styles')]: path.join(
229+
chaireLibFrontendRoot,
230+
'src',
231+
'styles'
232+
),
233+
[path.join(transitionFrontendRoot, 'lib', 'styles')]: path.join(
234+
transitionFrontendRoot,
235+
'src',
236+
'styles'
237+
)
238+
},
239+
fallback: { path: false }
189240
},
190241
devtool: isProduction ? 'cheap-source-map' : 'eval-source-map',
191242
devServer: {

yarn.lock

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9479,11 +9479,16 @@ jws@^3.2.2:
94799479
jwa "^1.4.2"
94809480
safe-buffer "^5.0.1"
94819481

9482-
kdbush@^3.0.0, kdbush@^4.0.2:
9482+
kdbush@^3.0.0:
94839483
version "3.0.0"
94849484
resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0"
94859485
integrity sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==
94869486

9487+
kdbush@^4.0.2:
9488+
version "4.0.2"
9489+
resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-4.0.2.tgz#2f7b7246328b4657dd122b6c7f025fbc2c868e39"
9490+
integrity sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==
9491+
94879492
kind-of@^6.0.2, kind-of@^6.0.3:
94889493
version "6.0.3"
94899494
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
@@ -10359,9 +10364,9 @@ min-indent@^1.0.0:
1035910364
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
1036010365

1036110366
mini-css-extract-plugin@^2.9.4:
10362-
version "2.9.4"
10363-
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.4.tgz#cafa1a42f8c71357f49cd1566810d74ff1cb0200"
10364-
integrity sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==
10367+
version "2.10.0"
10368+
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.0.tgz#d801a1f388f8fac7333c01b7c15c9222c811def4"
10369+
integrity sha512-540P2c5dYnJlyJxTaSloliZexv8rji6rY8FhQN+WF/82iHQfA23j/xtJx97L+mXOML27EqksSek/g4eK7jaL3g==
1036510370
dependencies:
1036610371
schema-utils "^4.0.0"
1036710372
tapable "^2.2.1"
@@ -12853,6 +12858,11 @@ strnum@^2.1.0:
1285312858
resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.2.tgz#a5e00ba66ab25f9cafa3726b567ce7a49170937a"
1285412859
integrity sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==
1285512860

12861+
style-loader@^4.0.0:
12862+
version "4.0.0"
12863+
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-4.0.0.tgz#0ea96e468f43c69600011e0589cb05c44f3b17a5"
12864+
integrity sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==
12865+
1285612866
style-to-js@^1.0.0:
1285712867
version "1.1.21"
1285812868
resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.21.tgz#2908941187f857e79e28e9cd78008b9a0b3e0e8d"

0 commit comments

Comments
 (0)