Skip to content

Commit c7b20a0

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.
1 parent 8d7f411 commit c7b20a0

File tree

3 files changed

+93
-39
lines changed

3 files changed

+93
-39
lines changed

packages/transition-frontend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"i18next": "^25.7.3",
4141
"lodash": "^4.17.23",
4242
"maplibre-gl": "^5.16.0",
43+
"mini-css-extract-plugin": "^2.9.4",
4344
"moment": "^2.30.1",
4445
"papaparse": "^5.5.3",
4546
"rc-menu": "^9.16.1",
@@ -90,11 +91,11 @@
9091
"html-webpack-plugin": "^5.6.5",
9192
"jest": "^30.2.0",
9293
"json-loader": "^0.5.7",
93-
"mini-css-extract-plugin": "^2.9.4",
9494
"prettier-eslint-cli": "^8.0.1",
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: 83 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,31 @@ 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+
new RegExp(
103+
`(node_modules\\/(?!chaire-lib-frontend)|${path
104+
.join(chaireLibFrontendRoot, 'lib', 'styles')
105+
.replace(/\\/g, '/')
106+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}|${path
107+
.join(transitionFrontendRoot, 'lib', 'styles')
108+
.replace(/\\/g, '/')
109+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`
110+
),
82111
aggregateTimeout: 600
83112
},
84113
module: {
85114
rules: [
86115
{
87116
test: /\.tsx?$/,
88117
use: 'ts-loader',
89-
exclude: /node_modules/,
118+
exclude: /node_modules/
90119
},
91120
{
92121
use: 'json-loader',
@@ -104,7 +133,7 @@ module.exports = (env) => {
104133
{
105134
test: /\.s?css$/,
106135
use: [
107-
MiniCssExtractPlugin.loader,
136+
styleLoader, // In dev, style-loader injects CSS via <style> so SCSS changes apply after reload without a separate .css file
108137
{
109138
loader: 'css-loader',
110139
options: {
@@ -124,7 +153,7 @@ module.exports = (env) => {
124153
loader: '@alienfast/i18next-loader',
125154
options: {
126155
basenameAsNamespace: true,
127-
overrides: (fs.existsSync('./' + customLocalesFilePath) ? ['../' + customLocalesFilePath] : [])
156+
overrides: fs.existsSync('./' + customLocalesFilePath) ? ['../' + customLocalesFilePath] : []
128157
}
129158
}
130159
]
@@ -133,32 +162,38 @@ module.exports = (env) => {
133162
new CleanWebpackPlugin({
134163
dry: !isProduction,
135164
verbose: true,
136-
cleanAfterEveryBuildPatterns: ['**/*', '!images/**', '!*.html'],
165+
cleanAfterEveryBuildPatterns: ['**/*', '!images/**', '!*.html']
137166
}),
138167
new HtmlWebpackPlugin({
139168
filename: htmlFileName,
140-
template: path.join(publicDirectory, 'index.html'),
169+
template: path.join(publicDirectory, 'index.html')
141170
}),
142171
new MiniCssExtractPlugin({
143172
filename: styleFileName
144173
}),
145174
new webpack.DefinePlugin({
146175
'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)
176+
IS_BROWSER: JSON.stringify(true),
177+
HOST: JSON.stringify(process.env.HOST),
178+
TRROUTING_HOST: JSON.stringify(process.env.TRROUTING_HOST),
179+
PROJECT_SOURCE: JSON.stringify(process.env.PROJECT_SOURCE),
180+
IS_TESTING: JSON.stringify(process.env.NODE_ENV === 'test'),
181+
GOOGLE_API_KEY: JSON.stringify(process.env.GOOGLE_API_KEY),
182+
CUSTOM_RASTER_TILES_XYZ_URL: JSON.stringify(
183+
process.env.CUSTOM_RASTER_TILES_XYZ_URL || config.customRasterTilesXyzUrl
184+
),
185+
CUSTOM_RASTER_TILES_MIN_ZOOM: JSON.stringify(
186+
process.env.CUSTOM_RASTER_TILES_MIN_ZOOM || config.customRasterTilesMinZoom
187+
),
188+
CUSTOM_RASTER_TILES_MAX_ZOOM: JSON.stringify(
189+
process.env.CUSTOM_RASTER_TILES_MAX_ZOOM || config.customRasterTilesMaxZoom
190+
)
156191
},
157-
'__CONFIG__': JSON.stringify({
192+
__CONFIG__: JSON.stringify({
158193
...config
159194
})
160195
}),
161-
new webpack.optimize.AggressiveMergingPlugin(),//Merge chunks
196+
new webpack.optimize.AggressiveMergingPlugin(), //Merge chunks
162197
new CompressionPlugin({
163198
filename: '[path][base].gz[query]',
164199
algorithm: 'gzip',
@@ -168,24 +203,37 @@ module.exports = (env) => {
168203
}),
169204
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, new RegExp(languagesFilter)),
170205
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-
)
206+
new CopyWebpackPlugin({
207+
patterns: [
208+
{
209+
context: path.join(__dirname, 'lib', 'assets'),
210+
from: '**/*',
211+
to: '',
212+
noErrorOnMissing: true
213+
}
214+
]
215+
})
183216
],
184217
resolve: {
185218
mainFields: ['browser', 'main', 'module'],
186219
modules: ['node_modules'],
187220
extensions: ['.json', '.js', '.ts', '.tsx'],
188-
fallback: { path: false },
221+
// In dev, read SCSS from chaire-lib-frontend and transition-frontend source so changes apply without running copy-files
222+
alias: isProduction
223+
? {}
224+
: {
225+
[path.join(chaireLibFrontendRoot, 'lib', 'styles')]: path.join(
226+
chaireLibFrontendRoot,
227+
'src',
228+
'styles'
229+
),
230+
[path.join(transitionFrontendRoot, 'lib', 'styles')]: path.join(
231+
transitionFrontendRoot,
232+
'src',
233+
'styles'
234+
)
235+
},
236+
fallback: { path: false }
189237
},
190238
devtool: isProduction ? 'cheap-source-map' : 'eval-source-map',
191239
devServer: {

yarn.lock

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10359,9 +10359,9 @@ min-indent@^1.0.0:
1035910359
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
1036010360

1036110361
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==
10362+
version "2.10.0"
10363+
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.0.tgz#d801a1f388f8fac7333c01b7c15c9222c811def4"
10364+
integrity sha512-540P2c5dYnJlyJxTaSloliZexv8rji6rY8FhQN+WF/82iHQfA23j/xtJx97L+mXOML27EqksSek/g4eK7jaL3g==
1036510365
dependencies:
1036610366
schema-utils "^4.0.0"
1036710367
tapable "^2.2.1"
@@ -12853,6 +12853,11 @@ strnum@^2.1.0:
1285312853
resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.2.tgz#a5e00ba66ab25f9cafa3726b567ce7a49170937a"
1285412854
integrity sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==
1285512855

12856+
style-loader@^4.0.0:
12857+
version "4.0.0"
12858+
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-4.0.0.tgz#0ea96e468f43c69600011e0589cb05c44f3b17a5"
12859+
integrity sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==
12860+
1285612861
style-to-js@^1.0.0:
1285712862
version "1.1.21"
1285812863
resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.21.tgz#2908941187f857e79e28e9cd78008b9a0b3e0e8d"

0 commit comments

Comments
 (0)