Skip to content

Commit 6b07940

Browse files
Refactor: Add style-loader dependency and update webpack configurations
Fix: #1403 - Add style-loader dependency and update webpack configurations - Adjusted watch options to improve rebuild efficiency for specific directories. Note: it still doesn't hot reloading in dev mode, but we just need to refresh the page now to see any frontend changes.
1 parent 0e902b7 commit 6b07940

File tree

8 files changed

+105
-8
lines changed

8 files changed

+105
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased (0.6.0)] - YYYY-MM-DD
99

1010
### Added
11+
- **CSS refresh in dev mode**: Changes to `.scss` files are automatically picked up by `yarn build:dev` and apply after a page reload (fixes [#1403](https://github.com/chairemobilite/evolution/issues/1403)). Survey projects that want the same: see [#1407](https://github.com/chairemobilite/evolution/pull/1407) for the webpack modifications.
1112
- Added `maxAccessEgressTravelTimeMinutes` and `walkingSpeedKmPerHour` to accessibility map calculation parameters (#1379)
1213

1314
### Changed
@@ -21,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2122
### Security
2223

2324
### Dependency updates
25+
- style-loader: 4.0.0
2426
- lodash: 4.17.21 => 4.17.23
2527
- @types/lodash: 4.17.21 => 4.17.23
2628
- yargs: 17.7.2 => 18.0.0

example/demo_generator/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"cross-env": "^10.1.0",
5252
"eslint": "^8.57.1",
5353
"prettier-eslint-cli": "^8.0.1",
54+
"style-loader": "^4.0.0",
5455
"typescript": "^5.9.3"
5556
}
5657
}

example/demo_generator/webpack.admin.config.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
99
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
1010
const CompressionPlugin = require('compression-webpack-plugin');
1111

12+
// Ensure server config is found regardless of cwd
13+
if (!process.env.PROJECT_CONFIG) {
14+
process.env.PROJECT_CONFIG = path.join(__dirname, 'config.js');
15+
}
1216
require('chaire-lib-backend/lib/config/dotenv.config');
1317

1418
if (!process.env.NODE_ENV) {
@@ -47,6 +51,10 @@ module.exports = (env) => {
4751
const isProduction = process.env.NODE_ENV === 'production';
4852
console.log('process.env.NODE_ENV', process.env.NODE_ENV);
4953

54+
// In dev, style-loader injects CSS via <style> so SCSS changes apply after reload
55+
const styleLoader = isProduction ? MiniCssExtractPlugin.loader : 'style-loader';
56+
const evolutionFrontendRoot = path.dirname(require.resolve('evolution-frontend/package.json'));
57+
5058
const languages = config.languages || ['fr', 'en'];
5159
const momentLanguagesFilter = `/${languages.join('|')}/`;
5260

@@ -82,7 +90,17 @@ module.exports = (env) => {
8290
publicPath: '/dist/'
8391
},
8492
watchOptions: {
85-
ignored: ['node_modules/**'],
93+
// In dev, watch evolution-frontend and evolution-common so CSS/TS changes trigger rebuild
94+
// Exclude lib/styles from evolution-frontend since we use alias to point to src/styles
95+
ignored: isProduction
96+
// In production, ignore all node_modules to avoid rebuilding the whole project
97+
? new RegExp('node_modules/')
98+
// Ignore all node_modules except evolution-frontend and evolution-common,
99+
// and also specifically ignore the lib/styles directory of evolution-frontend
100+
// (which we override via an alias to our src/styles).
101+
: new RegExp(
102+
`(node_modules\\/(?!evolution-frontend|evolution-common)|${path.join(evolutionFrontendRoot, 'lib', 'styles').replace(/\\/g, '/').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`
103+
),
86104
aggregateTimeout: 600
87105
},
88106
module: {
@@ -103,7 +121,7 @@ module.exports = (env) => {
103121
{
104122
test: /\.s?css$/,
105123
use: [
106-
MiniCssExtractPlugin.loader,
124+
styleLoader,
107125
{
108126
loader: 'css-loader',
109127
options: {
@@ -213,6 +231,10 @@ module.exports = (env) => {
213231
mainFields: ['browser', 'main', 'module'],
214232
modules: ['node_modules'],
215233
extensions: ['.json', '.js', '.ts', '.tsx'],
234+
// In dev, read SCSS from evolution-frontend source so changes apply without running copy-files
235+
alias: isProduction ? {} : {
236+
[path.join(evolutionFrontendRoot, 'lib', 'styles')]: path.join(evolutionFrontendRoot, 'src', 'styles')
237+
},
216238
// These modules are not used in the frontend, don't try to resolve them as they are nodejs only and don't have a browser counterpart (but they may be used in transition-legacy which is still not cleanly separated)
217239
fallback: { path: false, buffer: false }
218240
},

example/demo_generator/webpack.config.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
99
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
1010
const CompressionPlugin = require('compression-webpack-plugin');
1111

12+
// Ensure server config is found regardless of cwd (fixes serve:dev when run from any directory)
13+
if (!process.env.PROJECT_CONFIG) {
14+
process.env.PROJECT_CONFIG = path.join(__dirname, 'config.js');
15+
}
1216
require('chaire-lib-backend/lib/config/dotenv.config');
1317

1418
if (!process.env.NODE_ENV) {
@@ -30,13 +34,17 @@ module.exports = (env) => {
3034
const isProduction = process.env.NODE_ENV === 'production';
3135
console.log('process.env.NODE_ENV', process.env.NODE_ENV);
3236

37+
// In dev, style-loader injects CSS via <style> so SCSS changes apply after reload without a separate .css file
38+
const styleLoader = isProduction ? MiniCssExtractPlugin.loader : 'style-loader';
39+
3340
const languages = config.languages || ['fr', 'en'];
3441
const momentLanguagesFilter = `/${languages.join('|')}/`;
3542

3643
const entryFileName = './lib/app-survey.js';
3744
const customStylesFilePath = `${__dirname}/lib/styles/styles.scss`;
3845
const customLocalesFilePath = `${__dirname}/locales`;
3946
const entry = [entryFileName, customStylesFilePath];
47+
const evolutionFrontendRoot = path.dirname(require.resolve('evolution-frontend/package.json'));
4048
const includeDirectories = [
4149
path.join(__dirname, 'lib', 'survey'),
4250

@@ -64,7 +72,17 @@ module.exports = (env) => {
6472
publicPath: '/dist/'
6573
},
6674
watchOptions: {
67-
ignored: ['node_modules/**'],
75+
// In dev, watch evolution-frontend and evolution-common so CSS/TS changes trigger rebuild
76+
// Exclude lib/styles from evolution-frontend since we use alias to point to src/styles
77+
ignored: isProduction
78+
// In production, ignore all node_modules to avoid rebuilding the whole project
79+
? new RegExp('node_modules/')
80+
// Ignore all node_modules except evolution-frontend and evolution-common,
81+
// and also specifically ignore the lib/styles directory of evolution-frontend
82+
// (which we override via an alias to our src/styles).
83+
: new RegExp(
84+
`(node_modules\\/(?!evolution-frontend|evolution-common)|${path.join(evolutionFrontendRoot, 'lib', 'styles').replace(/\\/g, '/').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`
85+
),
6886
aggregateTimeout: 600
6987
},
7088
module: {
@@ -85,7 +103,7 @@ module.exports = (env) => {
85103
{
86104
test: /\.s?css$/,
87105
use: [
88-
MiniCssExtractPlugin.loader,
106+
styleLoader, // In dev, style-loader injects CSS via <style> so SCSS changes apply after reload without a separate .css file
89107
{
90108
loader: 'css-loader',
91109
options: {
@@ -195,6 +213,10 @@ module.exports = (env) => {
195213
mainFields: ['browser', 'main', 'module'],
196214
modules: ['node_modules'],
197215
extensions: ['.json', '.js', '.ts', '.tsx'],
216+
// In dev, read SCSS from evolution-frontend source so changes apply without running copy-files
217+
alias: isProduction ? {} : {
218+
[path.join(evolutionFrontendRoot, 'lib', 'styles')]: path.join(evolutionFrontendRoot, 'src', 'styles')
219+
},
198220
// These modules are not used in the frontend, don't try to resolve them as they are nodejs only and don't have a browser counterpart (but they may be used in transition-legacy which is still not cleanly separated)
199221
fallback: { path: false, buffer: false }
200222
},

example/demo_survey/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"mini-css-extract-plugin": "^2.9.4",
6868
"sass": "^1.97.0",
6969
"sass-loader": "^16.0.6",
70+
"style-loader": "^4.0.0",
7071
"source-map-loader": "^5.0.0",
7172
"ts-loader": "^9.5.4",
7273
"ts-shader-loader": "^2.0.2",

example/demo_survey/webpack.admin.config.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
77
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
88
const CompressionPlugin = require('compression-webpack-plugin');
99

10+
// Ensure server config is found regardless of cwd
11+
if (!process.env.PROJECT_CONFIG) {
12+
process.env.PROJECT_CONFIG = path.join(__dirname, 'config.js');
13+
}
1014
require('chaire-lib-backend/lib/config/dotenv.config');
1115

1216
if (!process.env.NODE_ENV) {
@@ -43,6 +47,10 @@ module.exports = (env) => {
4347
const isProduction = process.env.NODE_ENV === 'production';
4448
console.log('process.env.NODE_ENV', process.env.NODE_ENV);
4549

50+
// In dev, style-loader injects CSS via <style> so SCSS changes apply after reload
51+
const styleLoader = isProduction ? MiniCssExtractPlugin.loader : 'style-loader';
52+
const evolutionFrontendRoot = path.dirname(require.resolve('evolution-frontend/package.json'));
53+
4654
const languages = config.languages || ['fr', 'en'];
4755
const momentLanguagesFilter = `/${languages.join("|")}/`;
4856

@@ -76,7 +84,17 @@ module.exports = (env) => {
7684
publicPath: '/dist/'
7785
},
7886
watchOptions: {
79-
ignored: ['node_modules/**'],
87+
// In dev, watch evolution-frontend and evolution-common so CSS/TS changes trigger rebuild
88+
// Exclude lib/styles from evolution-frontend since we use alias to point to src/styles
89+
ignored: isProduction
90+
// In production, ignore all node_modules to avoid rebuilding the whole project
91+
? new RegExp('node_modules/')
92+
// Ignore all node_modules except evolution-frontend and evolution-common,
93+
// and also specifically ignore the lib/styles directory of evolution-frontend
94+
// (which we override via an alias to our src/styles).
95+
: new RegExp(
96+
`(node_modules\\/(?!evolution-frontend|evolution-common)|${path.join(evolutionFrontendRoot, 'lib', 'styles').replace(/\\/g, '/').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`
97+
),
8098
aggregateTimeout: 600
8199
},
82100
module: {
@@ -97,7 +115,7 @@ module.exports = (env) => {
97115
{
98116
test: /\.s?css$/,
99117
use: [
100-
MiniCssExtractPlugin.loader,
118+
styleLoader,
101119
{
102120
loader: 'css-loader',
103121
options: {
@@ -199,6 +217,10 @@ module.exports = (env) => {
199217
mainFields: ['browser', 'main', 'module'],
200218
modules: ['node_modules'],
201219
extensions: ['.json', '.js', '.ts', '.tsx'],
220+
// In dev, read SCSS from evolution-frontend source so changes apply without running copy-files
221+
alias: isProduction ? {} : {
222+
[path.join(evolutionFrontendRoot, 'lib', 'styles')]: path.join(evolutionFrontendRoot, 'src', 'styles')
223+
},
202224
// These modules are not used in the frontend, don't try to resolve them as they are nodejs only and don't have a browser counterpart (but they may be used in transition-legacy which is still not cleanly separated)
203225
fallback: { path: false, buffer: false }
204226
},

example/demo_survey/webpack.config.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
77
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
88
const CompressionPlugin = require('compression-webpack-plugin');
99

10+
// Ensure server config is found regardless of cwd (fixes serve:dev when run from any directory)
11+
if (!process.env.PROJECT_CONFIG) {
12+
process.env.PROJECT_CONFIG = path.join(__dirname, 'config.js');
13+
}
1014
require('chaire-lib-backend/lib/config/dotenv.config');
1115

1216
if (!process.env.NODE_ENV) {
@@ -26,11 +30,15 @@ module.exports = (env) => {
2630
const isProduction = process.env.NODE_ENV === 'production';
2731
console.log('process.env.NODE_ENV', process.env.NODE_ENV);
2832

33+
// In dev, style-loader injects CSS via <style> so SCSS changes apply after reload without a separate .css file
34+
const styleLoader = isProduction ? MiniCssExtractPlugin.loader : 'style-loader';
35+
2936
const languages = config.languages || ['fr', 'en'];
3037
const momentLanguagesFilter = `/${languages.join("|")}/`;
3138

3239
const customStylesFilePath = `${__dirname}/lib/styles/styles.scss`;
3340
const customLocalesFilePath = `${__dirname}/locales`;
41+
const evolutionFrontendRoot = path.dirname(require.resolve('evolution-frontend/package.json'));
3442
const includeDirectories = [
3543
path.join(__dirname, 'lib', 'survey'),
3644

@@ -60,7 +68,17 @@ module.exports = (env) => {
6068
publicPath: '/dist/'
6169
},
6270
watchOptions: {
63-
ignored: ['node_modules/**'],
71+
// In dev, watch evolution-frontend and evolution-common so CSS/TS changes trigger rebuild
72+
// Exclude lib/styles from evolution-frontend since we use alias to point to src/styles
73+
ignored: isProduction
74+
// In production, ignore all node_modules to avoid rebuilding the whole project
75+
? new RegExp('node_modules/')
76+
// Ignore all node_modules except evolution-frontend and evolution-common,
77+
// and also specifically ignore the lib/styles directory of evolution-frontend
78+
// (which we override via an alias to our src/styles).
79+
: new RegExp(
80+
`(node_modules\\/(?!evolution-frontend|evolution-common)|${path.join(evolutionFrontendRoot, 'lib', 'styles').replace(/\\/g, '/').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`
81+
),
6482
aggregateTimeout: 600
6583
},
6684
module: {
@@ -81,7 +99,7 @@ module.exports = (env) => {
8199
{
82100
test: /\.s?css$/,
83101
use: [
84-
MiniCssExtractPlugin.loader,
102+
styleLoader, // In dev, style-loader injects CSS via <style> so SCSS changes apply after reload without a separate .css file
85103
{
86104
loader: 'css-loader',
87105
options: {
@@ -194,6 +212,10 @@ module.exports = (env) => {
194212
mainFields: ['browser', 'main', 'module'],
195213
modules: ['node_modules'],
196214
extensions: ['.json', '.js', '.ts', '.tsx'],
215+
// In dev, read SCSS from evolution-frontend source so changes apply without running copy-files
216+
alias: isProduction ? {} : {
217+
[path.join(evolutionFrontendRoot, 'lib', 'styles')]: path.join(evolutionFrontendRoot, 'src', 'styles')
218+
},
197219
// These modules are not used in the frontend, don't try to resolve them as they are nodejs only and don't have a browser counterpart (but they may be used in transition-legacy which is still not cleanly separated)
198220
fallback: { path: false, buffer: false }
199221
},

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13344,6 +13344,11 @@ strnum@^2.1.0:
1334413344
resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.2.tgz#a5e00ba66ab25f9cafa3726b567ce7a49170937a"
1334513345
integrity sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==
1334613346

13347+
style-loader@^4.0.0:
13348+
version "4.0.0"
13349+
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-4.0.0.tgz#0ea96e468f43c69600011e0589cb05c44f3b17a5"
13350+
integrity sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==
13351+
1334713352
style-to-object@^1.0.0:
1334813353
version "1.0.8"
1334913354
resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.8.tgz#67a29bca47eaa587db18118d68f9d95955e81292"

0 commit comments

Comments
 (0)