Merge pull request #18318 from vector-im/palid/feature/css-hmr

Add CSS hot module reload
This commit is contained in:
Dariusz Niemczyk 2021-08-04 11:07:05 +02:00 committed by GitHub
commit 8b56a55906
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 241 additions and 49 deletions

14
.env.example Normal file
View file

@ -0,0 +1,14 @@
# If you want to have proper hot-reload css experience, define one and set this on.
CSS_HOT_RELOAD=1
# Define which one theme you want to load for hot-reload purposes.
# To use a single theme just uncomment a line with the theme you want to use.
MATRIX_THEMES='light'
#MATRIX_THEMES='dark'
#MATRIX_THEMES='light-legacy'
#MATRIX_THEMES='dark-legacy'
#MATRIX_THEMES='light-custom'
#MATRIX_THEMES='dark-custom'
# You can load multiple themes at once, but switching between them may require full page reload.
# It will also multiple compliation times by the number of turned on themes.
# If you want to use multiple themes, define the combinations manually like below:
#MATRIX_THEMES='light,dark'

1
.gitignore vendored
View file

@ -21,3 +21,4 @@ electron/pub
/webpack-stats.json /webpack-stats.json
.vscode .vscode
.vscode/ .vscode/
.env

View file

@ -30,14 +30,14 @@ and [element-ios](https://github.com/vector-im/element-ios).
Getting Started Getting Started
=============== ===============
The easiest way to test Element is to just use the hosted copy at https://app.element.io. The easiest way to test Element is to just use the hosted copy at <https://app.element.io>.
The `develop` branch is continuously deployed to https://develop.element.io The `develop` branch is continuously deployed to <https://develop.element.io>
for those who like living dangerously. for those who like living dangerously.
To host your own copy of Element, the quickest bet is to use a pre-built To host your own copy of Element, the quickest bet is to use a pre-built
released version of Element: released version of Element:
1. Download the latest version from https://github.com/vector-im/element-web/releases 1. Download the latest version from <https://github.com/vector-im/element-web/releases>
1. Untar the tarball on your web server 1. Untar the tarball on your web server
1. Move (or symlink) the `element-x.x.x` directory to an appropriate name 1. Move (or symlink) the `element-x.x.x` directory to an appropriate name
1. Configure the correct caching headers in your webserver (see below) 1. Configure the correct caching headers in your webserver (see below)
@ -46,7 +46,7 @@ released version of Element:
1. Enter the URL into your browser and log into Element! 1. Enter the URL into your browser and log into Element!
Releases are signed using gpg and the OpenPGP standard, and can be checked against the public key located Releases are signed using gpg and the OpenPGP standard, and can be checked against the public key located
at https://packages.riot.im/element-release-key.asc. at <https://packages.riot.im/element-release-key.asc>.
Note that for the security of your chats will need to serve Element Note that for the security of your chats will need to serve Element
over HTTPS. Major browsers also do not allow you to use VoIP/video over HTTPS. Major browsers also do not allow you to use VoIP/video
@ -72,7 +72,7 @@ access to Element (or other apps) due to sharing the same domain.
We have put some coarse mitigations into place to try to protect against this We have put some coarse mitigations into place to try to protect against this
situation, but it's still not good practice to do it in the first place. See situation, but it's still not good practice to do it in the first place. See
https://github.com/vector-im/element-web/issues/1977 for more details. <https://github.com/vector-im/element-web/issues/1977> for more details.
Configuration best practices Configuration best practices
---------------------------- ----------------------------
@ -80,15 +80,15 @@ Configuration best practices
Unless you have special requirements, you will want to add the following to Unless you have special requirements, you will want to add the following to
your web server configuration when hosting Element Web: your web server configuration when hosting Element Web:
- The `X-Frame-Options: SAMEORIGIN` header, to prevent Element Web from being * The `X-Frame-Options: SAMEORIGIN` header, to prevent Element Web from being
framed and protect from [clickjacking][owasp-clickjacking]. framed and protect from [clickjacking][owasp-clickjacking].
- The `frame-ancestors 'none'` directive to your `Content-Security-Policy` * The `frame-ancestors 'none'` directive to your `Content-Security-Policy`
header, as the modern replacement for `X-Frame-Options` (though both should be header, as the modern replacement for `X-Frame-Options` (though both should be
included since not all browsers support it yet, see included since not all browsers support it yet, see
[this][owasp-clickjacking-csp]). [this][owasp-clickjacking-csp]).
- The `X-Content-Type-Options: nosniff` header, to [disable MIME * The `X-Content-Type-Options: nosniff` header, to [disable MIME
sniffing][mime-sniffing]. sniffing][mime-sniffing].
- The `X-XSS-Protection: 1; mode=block;` header, for basic XSS protection in * The `X-XSS-Protection: 1; mode=block;` header, for basic XSS protection in
legacy browsers. legacy browsers.
[mime-sniffing]: [mime-sniffing]:
@ -127,10 +127,10 @@ guide](https://classic.yarnpkg.com/en/docs/install) if you do not have it alread
1. Clone the repo: `git clone https://github.com/vector-im/element-web.git`. 1. Clone the repo: `git clone https://github.com/vector-im/element-web.git`.
1. Switch to the element-web directory: `cd element-web`. 1. Switch to the element-web directory: `cd element-web`.
1. Install the prerequisites: `yarn install`. 1. Install the prerequisites: `yarn install`.
* If you're using the `develop` branch, then it is recommended to set up a * If you're using the `develop` branch, then it is recommended to set up a
proper development environment (see [Setting up a dev proper development environment (see [Setting up a dev
environment](#setting-up-a-dev-environment) below). Alternatively, you environment](#setting-up-a-dev-environment) below). Alternatively, you
can use https://develop.element.io - the continuous integration release of can use <https://develop.element.io> - the continuous integration release of
the develop branch. the develop branch.
1. Configure the app by copying `config.sample.json` to `config.json` and 1. Configure the app by copying `config.sample.json` to `config.json` and
modifying it. See the [configuration docs](docs/config.md) for details. modifying it. See the [configuration docs](docs/config.md) for details.
@ -148,15 +148,16 @@ Running as a Desktop app
======================== ========================
Element can also be run as a desktop app, wrapped in Electron. You can download a Element can also be run as a desktop app, wrapped in Electron. You can download a
pre-built version from https://element.io/get-started or, if you prefer, pre-built version from <https://element.io/get-started> or, if you prefer,
build it yourself. build it yourself.
To build it yourself, follow the instructions at https://github.com/vector-im/element-desktop. To build it yourself, follow the instructions at <https://github.com/vector-im/element-desktop>.
Many thanks to @aviraldg for the initial work on the Electron integration. Many thanks to @aviraldg for the initial work on the Electron integration.
Other options for running as a desktop app: Other options for running as a desktop app:
* @asdf:matrix.org points out that you can use nativefier and it just works(tm)
* @asdf:matrix.org points out that you can use nativefier and it just works(tm)
```bash ```bash
yarn global add nativefier yarn global add nativefier
@ -171,6 +172,7 @@ Running from Docker
The Docker image can be used to serve element-web as a web server. The easiest way to use The Docker image can be used to serve element-web as a web server. The easiest way to use
it is to use the prebuilt image: it is to use the prebuilt image:
```bash ```bash
docker run -p 80:80 vectorim/element-web docker run -p 80:80 vectorim/element-web
``` ```
@ -178,11 +180,13 @@ docker run -p 80:80 vectorim/element-web
To supply your own custom `config.json`, map a volume to `/app/config.json`. For example, To supply your own custom `config.json`, map a volume to `/app/config.json`. For example,
if your custom config was located at `/etc/element-web/config.json` then your Docker command if your custom config was located at `/etc/element-web/config.json` then your Docker command
would be: would be:
```bash ```bash
docker run -p 80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web docker run -p 80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web
``` ```
To build the image yourself: To build the image yourself:
```bash ```bash
git clone https://github.com/vector-im/element-web.git element-web git clone https://github.com/vector-im/element-web.git element-web
cd element-web cd element-web
@ -192,6 +196,7 @@ docker build .
If you're building a custom branch, or want to use the develop branch, check out the appropriate If you're building a custom branch, or want to use the develop branch, check out the appropriate
element-web branch and then run: element-web branch and then run:
```bash ```bash
docker build -t \ docker build -t \
--build-arg USE_CUSTOM_SDKS=true \ --build-arg USE_CUSTOM_SDKS=true \
@ -224,6 +229,7 @@ Caching requirements
==================== ====================
Element requires the following URLs not to be cached, when/if you are serving Element from your own webserver: Element requires the following URLs not to be cached, when/if you are serving Element from your own webserver:
``` ```
/config.*.json /config.*.json
/i18n /i18n
@ -261,6 +267,10 @@ internet. So please don't depend on resources (JS libs, CSS, images, fonts)
hosted by external CDNs or servers but instead please package all dependencies hosted by external CDNs or servers but instead please package all dependencies
into Element itself. into Element itself.
CSS hot-reload is currently an opt-in development feature, and if you want to have
it working properly on your environment, create a `.env` file in this repository
with proper environmental, see `.env.example` for documentation and example.
Setting up a dev environment Setting up a dev environment
============================ ============================
@ -302,14 +312,15 @@ yarn reskindex
yarn start yarn start
``` ```
Wait a few seconds for the initial build to finish; you should see something like: Wait a few seconds for the initial build to finish; you should see something like:
``` ```
[element-js] <s> [webpack.Progress] 100% [element-js] <s> [webpack.Progress] 100%
[element-js] [element-js]
[element-js] 「wdm」: 1840 modules [element-js] 「wdm」: 1840 modules
[element-js] 「wdm」: Compiled successfully. [element-js] 「wdm」: Compiled successfully.
``` ```
Remember, the command will not terminate since it runs the web server Remember, the command will not terminate since it runs the web server
and rebuilds source files when they change. This development server also and rebuilds source files when they change. This development server also
disables caching, so do NOT use it in production. disables caching, so do NOT use it in production.
@ -317,7 +328,7 @@ Wait a few seconds for the initial build to finish; you should see something lik
Configure the app by copying `config.sample.json` to `config.json` and Configure the app by copying `config.sample.json` to `config.json` and
modifying it. See the [configuration docs](docs/config.md) for details. modifying it. See the [configuration docs](docs/config.md) for details.
Open http://127.0.0.1:8080/ in your browser to see your newly built Element. Open <http://127.0.0.1:8080/> in your browser to see your newly built Element.
**Note**: The build script uses inotify by default on Linux to monitor directories **Note**: The build script uses inotify by default on Linux to monitor directories
for changes. If the inotify limits are too low your build will fail silently or with for changes. If the inotify limits are too low your build will fail silently or with
@ -393,14 +404,14 @@ Triaging issues
We strive to completely cover all applicable issues with these core labels: We strive to completely cover all applicable issues with these core labels:
1. __Type__ — Every issue is assigned a type: 1. __Type__ — Every issue is assigned a type:
- __[T-Defect](https://github.com/vector-im/element-web/labels/T-Defect):__ * __[T-Defect](https://github.com/vector-im/element-web/labels/T-Defect):__
Bugs, crashes, hangs, vulnerabilities, or other reported problems Bugs, crashes, hangs, vulnerabilities, or other reported problems
- __[T-Enhancement](https://github.com/vector-im/element-web/labels/T-Enhancement):__ * __[T-Enhancement](https://github.com/vector-im/element-web/labels/T-Enhancement):__
New features, changes in functionality, performance boosts, user-facing New features, changes in functionality, performance boosts, user-facing
improvements improvements
- __[T-Task](https://github.com/vector-im/element-web/labels/T-Task):__ * __[T-Task](https://github.com/vector-im/element-web/labels/T-Task):__
Refactoring, enabling or disabling functionality, other engineering tasks Refactoring, enabling or disabling functionality, other engineering tasks
- __[T-Other](https://github.com/vector-im/element-web/labels/T-Other):__ * __[T-Other](https://github.com/vector-im/element-web/labels/T-Other):__
Questions, user support, anything else Questions, user support, anything else
2. __Severity__ — All issues labeled `T-Defect` are also assigned a severity: 2. __Severity__ — All issues labeled `T-Defect` are also assigned a severity:

View file

@ -46,7 +46,7 @@
"start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n reskindex,reskindex-react,res,element-js \"yarn reskindex:watch\" \"yarn reskindex:watch-react\" \"yarn start:res\" \"yarn start:js\"", "start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n reskindex,reskindex-react,res,element-js \"yarn reskindex:watch\" \"yarn reskindex:watch-react\" \"yarn start:res\" \"yarn start:js\"",
"start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n reskindex,reskindex-react,res,element-js \"yarn reskindex:watch\" \"yarn reskindex:watch-react\" \"yarn start:res\" \"yarn start:js --https\"", "start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n reskindex,reskindex-react,res,element-js \"yarn reskindex:watch\" \"yarn reskindex:watch-react\" \"yarn start:res\" \"yarn start:js --https\"",
"start:res": "yarn build:jitsi && node scripts/copy-res.js -w", "start:res": "yarn build:jitsi && node scripts/copy-res.js -w",
"start:js": "webpack-dev-server --host=0.0.0.0 --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js -w --progress --mode development --disable-host-check", "start:js": "webpack-dev-server --host=0.0.0.0 --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js -w --progress --mode development --disable-host-check --hot",
"lint": "yarn lint:types && yarn lint:js && yarn lint:style", "lint": "yarn lint:types && yarn lint:js && yarn lint:style",
"lint:js": "eslint --max-warnings 0 src", "lint:js": "eslint --max-warnings 0 src",
"lint:js-fix": "eslint --fix src", "lint:js-fix": "eslint --fix src",
@ -105,6 +105,7 @@
"concurrently": "^5.3.0", "concurrently": "^5.3.0",
"cpx": "^1.5.0", "cpx": "^1.5.0",
"css-loader": "^3.6.0", "css-loader": "^3.6.0",
"dotenv": "^10.0.0",
"eslint": "7.18.0", "eslint": "7.18.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-matrix-org": "github:matrix-org/eslint-plugin-matrix-org#2306b3d4da4eba908b256014b979f1d3d43d2945", "eslint-plugin-matrix-org": "github:matrix-org/eslint-plugin-matrix-org#2306b3d4da4eba908b256014b979f1d3d43d2945",
@ -142,6 +143,8 @@
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"shell-escape": "^0.2.0", "shell-escape": "^0.2.0",
"simple-proxy-agent": "^1.1.0", "simple-proxy-agent": "^1.1.0",
"string-replace-loader": "2",
"style-loader": "2",
"stylelint": "^13.9.0", "stylelint": "^13.9.0",
"stylelint-config-standard": "^20.0.0", "stylelint-config-standard": "^20.0.0",
"stylelint-scss": "^3.18.0", "stylelint-scss": "^3.18.0",

46
src/vector/devcss.ts Normal file
View file

@ -0,0 +1,46 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* This code will be autoremoved on production builds.
* The purpose of this code is that the webpack's `string-replace-loader`
* pretty much search for this string in this specific file and replaces it
* like a macro before any previous compilations, which allows us to inject
* some css requires statements that are specific to the themes we have turned
* on by ourselves. Without that very specific workaround, webpack would just
* import all the CSSes, which would make the whole thing useless, as on my
* machine with i9 the recompilation for all themes turned ou would take way
* over 30s, which is definitely too high for nice css reloads speed.
*
* For more details, see webpack.config.js:184 (string-replace-loader)
*/
if (process.env.NODE_ENV === 'development') {
("use theming");
/**
* Clean up old hot-module script injections as they hog up memory
* and anything other than the newest one is really not needed at all.
* We don't need to do it more frequently than every half a minute or so,
* but it's done to delay full page reload due to app slowness.
*/
setInterval(() => {
const elements = Array.from(document.querySelectorAll("script[src*=hot-update]"));
if (elements.length > 1) {
const oldInjects = elements.slice(0, elements.length - 1);
oldInjects.forEach(e => e.remove());
}
}, 1000);
}

View file

@ -43,7 +43,7 @@
if (match) { if (match) {
var title = match[1].charAt(0).toUpperCase() + match[1].slice(1); var title = match[1].charAt(0).toUpperCase() + match[1].slice(1);
%> %>
<link rel="stylesheet" disabled="disabled" title="<%= title %>" href="<%= file %>"> <link rel="stylesheet" disabled="disabled" data-mx-theme="<%= title %>" title="<%= title %>" href="<%= file %>">
<% } else { %> <% } else { %>
<link rel="stylesheet" href="<%= file %>"> <link rel="stylesheet" href="<%= file %>">
<% } <% }

View file

@ -25,6 +25,14 @@ require('gfm.css/gfm.css');
require('highlight.js/styles/github.css'); require('highlight.js/styles/github.css');
require('katex/dist/katex.css'); require('katex/dist/katex.css');
/**
* This require is necessary only for purposes of CSS hot reload, as otherwise
* webpack has some incredibly problems figuring out which css files should be
* hot reloaded, even with proper hints for the loader.
*
* On production build it's going to be an empty module, so don't worry about that.
*/
require('./devcss');
// These are things that can run before the skin loads - be careful not to reference the react-sdk though. // These are things that can run before the skin loads - be careful not to reference the react-sdk though.
import { parseQsFromFragment } from "./url_utils"; import { parseQsFromFragment } from "./url_utils";
import './modernizr'; import './modernizr';

View file

@ -1,18 +1,34 @@
/* eslint-disable quote-props */ /* eslint-disable quote-props */
const dotenv = require('dotenv');
const path = require('path'); const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackInjectPreload = require('@principalstudio/html-webpack-inject-preload'); const HtmlWebpackInjectPreload = require('@principalstudio/html-webpack-inject-preload');
dotenv.config();
let ogImageUrl = process.env.RIOT_OG_IMAGE_URL; let ogImageUrl = process.env.RIOT_OG_IMAGE_URL;
if (!ogImageUrl) ogImageUrl = 'https://app.element.io/themes/element/img/logos/opengraph.png'; if (!ogImageUrl) ogImageUrl = 'https://app.element.io/themes/element/img/logos/opengraph.png';
const additionalPlugins = [ const cssThemes = {
// This is where you can put your customisation replacements. // CSS themes
]; "theme-legacy": "./node_modules/matrix-react-sdk/res/themes/legacy-light/css/legacy-light.scss",
"theme-legacy-dark": "./node_modules/matrix-react-sdk/res/themes/legacy-dark/css/legacy-dark.scss",
"theme-light": "./node_modules/matrix-react-sdk/res/themes/light/css/light.scss",
"theme-dark": "./node_modules/matrix-react-sdk/res/themes/dark/css/dark.scss",
"theme-light-custom": "./node_modules/matrix-react-sdk/res/themes/light-custom/css/light-custom.scss",
"theme-dark-custom": "./node_modules/matrix-react-sdk/res/themes/dark-custom/css/dark-custom.scss",
};
function getActiveThemes() {
// We want to use `light` theme by default if it's not defined.
const theme = process.env.MATRIX_THEMES ?? 'dark';
const themes = theme.split(',').filter(x => x).map(x => x.trim()).filter(x => x);
return themes;
}
module.exports = (env, argv) => { module.exports = (env, argv) => {
let nodeEnv = argv.mode; let nodeEnv = argv.mode;
@ -29,6 +45,8 @@ module.exports = (env, argv) => {
// application to productions standards // application to productions standards
nodeEnv = "production"; nodeEnv = "production";
} }
const devMode = nodeEnv !== 'production';
const useCssHotReload = process.env.CSS_HOT_RELOAD === '1' && devMode;
const development = {}; const development = {};
if (argv.mode === "production") { if (argv.mode === "production") {
@ -45,9 +63,22 @@ module.exports = (env, argv) => {
const reactSdkSrcDir = path.resolve(require.resolve("matrix-react-sdk/package.json"), '..', 'src'); const reactSdkSrcDir = path.resolve(require.resolve("matrix-react-sdk/package.json"), '..', 'src');
const jsSdkSrcDir = path.resolve(require.resolve("matrix-js-sdk/package.json"), '..', 'src'); const jsSdkSrcDir = path.resolve(require.resolve("matrix-js-sdk/package.json"), '..', 'src');
const ACTIVE_THEMES = getActiveThemes();
function getThemesImports() {
const imports = ACTIVE_THEMES.map((t, index) => {
const themeImportPath = cssThemes[`theme-${ t }`].replace('./node_modules/', '');
return themeImportPath;
});
const s = JSON.stringify(ACTIVE_THEMES);
return `
window.MX_insertedThemeStylesCounter = 0;
window.MX_DEV_ACTIVE_THEMES = (${ s });
${ imports.map(i => `import("${ i }")`).join('\n') };
`;
}
return { return {
...development, ...development,
node: { node: {
// Mock out the NodeFS module: The opus decoder imports this wrongly. // Mock out the NodeFS module: The opus decoder imports this wrongly.
fs: 'empty', fs: 'empty',
@ -59,14 +90,7 @@ module.exports = (env, argv) => {
"jitsi": "./src/vector/jitsi/index.ts", "jitsi": "./src/vector/jitsi/index.ts",
"usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.js", "usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.js",
"recorder-worklet": "./node_modules/matrix-react-sdk/src/audio/RecorderWorklet.ts", "recorder-worklet": "./node_modules/matrix-react-sdk/src/audio/RecorderWorklet.ts",
...(useCssHotReload ? {} : cssThemes),
// CSS themes
"theme-legacy": "./node_modules/matrix-react-sdk/res/themes/legacy-light/css/legacy-light.scss",
"theme-legacy-dark": "./node_modules/matrix-react-sdk/res/themes/legacy-dark/css/legacy-dark.scss",
"theme-light": "./node_modules/matrix-react-sdk/res/themes/light/css/light.scss",
"theme-dark": "./node_modules/matrix-react-sdk/res/themes/dark/css/dark.scss",
"theme-light-custom": "./node_modules/matrix-react-sdk/res/themes/light-custom/css/light-custom.scss",
"theme-dark-custom": "./node_modules/matrix-react-sdk/res/themes/dark-custom/css/dark-custom.scss",
}, },
optimization: { optimization: {
@ -150,6 +174,14 @@ module.exports = (env, argv) => {
/olm[\\/](javascript[\\/])?olm\.js$/, /olm[\\/](javascript[\\/])?olm\.js$/,
], ],
rules: [ rules: [
useCssHotReload && {
test: /devcss\.ts$/,
loader: 'string-replace-loader',
options: {
search: '"use theming";',
replace: getThemesImports(),
},
},
{ {
test: /\.worker\.ts$/, test: /\.worker\.ts$/,
loader: "worker-loader", loader: "worker-loader",
@ -219,7 +251,7 @@ module.exports = (env, argv) => {
// It's important that this plugin is last otherwise we end // It's important that this plugin is last otherwise we end
// up with broken CSS. // up with broken CSS.
require('postcss-preset-env')({stage: 3, browsers: 'last 2 versions'}), require('postcss-preset-env')({ stage: 3, browsers: 'last 2 versions' }),
], ],
parser: "postcss-scss", parser: "postcss-scss",
"local-plugins": true, "local-plugins": true,
@ -230,7 +262,40 @@ module.exports = (env, argv) => {
{ {
test: /\.scss$/, test: /\.scss$/,
use: [ use: [
MiniCssExtractPlugin.loader, /**
* This code is hopeful that no .scss outside of our themes will be directly imported in any
* of the JS/TS files.
* Should be MUCH better with webpack 5, but we're stuck to this solution for now.
*/
useCssHotReload ? {
loader: 'style-loader',
/**
* If we refactor the `theme.js` in `matrix-react-sdk` a little bit,
* we could try using `lazyStyleTag` here to add and remove styles on demand,
* that would nicely resolve issues of race conditions for themes,
* at least for development purposes.
*/
options: {
insert: function insertBeforeAt(element) {
const parent = document.querySelector('head');
// We're in iframe
if (!window.MX_DEV_ACTIVE_THEMES) {
parent.appendChild(element);
return;
}
// Properly disable all other instances of themes
element.disabled = true;
element.onload = () => {
element.disabled = true;
};
const theme = window.MX_DEV_ACTIVE_THEMES[window.MX_insertedThemeStylesCounter];
element.setAttribute('data-mx-theme', theme);
window.MX_insertedThemeStylesCounter++;
parent.appendChild(element);
},
},
} : MiniCssExtractPlugin.loader,
{ {
loader: 'css-loader', loader: 'css-loader',
options: { options: {
@ -257,7 +322,7 @@ module.exports = (env, argv) => {
// It's important that this plugin is last otherwise we end // It's important that this plugin is last otherwise we end
// up with broken CSS. // up with broken CSS.
require('postcss-preset-env')({stage: 3, browsers: 'last 2 versions'}), require('postcss-preset-env')({ stage: 3, browsers: 'last 2 versions' }),
], ],
parser: "postcss-scss", parser: "postcss-scss",
"local-plugins": true, "local-plugins": true,
@ -370,13 +435,19 @@ module.exports = (env, argv) => {
}, },
], ],
}, },
], ].filter(Boolean),
}, },
plugins: [ plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'development', // use 'development' unless process.env.NODE_ENV is defined
DEBUG: false,
}),
// This exports our CSS using the splitChunks and loaders above. // This exports our CSS using the splitChunks and loaders above.
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
filename: 'bundles/[hash]/[name].css', filename: useCssHotReload ? "bundles/[name].css" : "bundles/[hash]/[name].css",
chunkFilename: useCssHotReload ? "bundles/[name].css" : "bundles/[hash]/[name].css",
ignoreOrder: false, // Enable to remove warnings about conflicting order ignoreOrder: false, // Enable to remove warnings about conflicting order
}), }),
@ -437,7 +508,6 @@ module.exports = (env, argv) => {
files: [{ match: /.*Inter.*\.woff2$/ }], files: [{ match: /.*Inter.*\.woff2$/ }],
}), }),
...additionalPlugins,
], ],
output: { output: {
@ -457,17 +527,17 @@ module.exports = (env, argv) => {
// configuration for the webpack-dev-server // configuration for the webpack-dev-server
devServer: { devServer: {
// serve unwebpacked assets from webapp. // serve unwebpacked assets from webapp.
contentBase: './webapp', contentBase: [
'./webapp',
],
// Only output errors, warnings, or new compilations. // Only output errors, warnings, or new compilations.
// This hides the massive list of modules. // This hides the massive list of modules.
stats: 'minimal', stats: 'minimal',
// hot: false,
// hot module replacement doesn't work (I think we'd need react-hot-reload?) // injectHot: false,
// so webpack-dev-server reloads the page on every update which is quite hotOnly: true,
// tedious in Riot since that can take a while. inline: true,
hot: false,
inline: false,
}, },
}; };
}; };

View file

@ -1555,7 +1555,7 @@
dependencies: dependencies:
"@types/istanbul-lib-report" "*" "@types/istanbul-lib-report" "*"
"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7": "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8":
version "7.0.8" version "7.0.8"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818"
integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==
@ -2020,7 +2020,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6" version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@ -4180,6 +4180,11 @@ dot-prop@^5.2.0:
dependencies: dependencies:
is-obj "^2.0.0" is-obj "^2.0.0"
dotenv@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
duplexer@^0.1.1: duplexer@^0.1.1:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
@ -7349,6 +7354,15 @@ loader-utils@^1.0.0, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4
emojis-list "^3.0.0" emojis-list "^3.0.0"
json5 "^1.0.1" json5 "^1.0.1"
loader-utils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
json5 "^2.1.2"
locate-path@^3.0.0: locate-path@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@ -10446,6 +10460,15 @@ schema-utils@^2.5.0, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7
ajv "^6.12.4" ajv "^6.12.4"
ajv-keywords "^3.5.2" ajv-keywords "^3.5.2"
schema-utils@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
dependencies:
"@types/json-schema" "^7.0.8"
ajv "^6.12.5"
ajv-keywords "^3.5.2"
select-hose@^2.0.0: select-hose@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@ -10968,6 +10991,14 @@ string-length@^4.0.1:
char-regex "^1.0.2" char-regex "^1.0.2"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
string-replace-loader@2:
version "2.3.0"
resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-2.3.0.tgz#7f29be7d73c94dd92eccd5c5a15651181d7ecd3d"
integrity sha512-HYBIHStViMKLZC/Lehxy42OuwsBaPzX/LjcF5mkJlE2SnHXmW6SW6eiHABTXnY8ZCm/REbdJ8qnA0ptmIzN0Ng==
dependencies:
loader-utils "^1.2.3"
schema-utils "^2.6.5"
string-width@^3.0.0, string-width@^3.1.0: string-width@^3.0.0, string-width@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
@ -11083,6 +11114,14 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
style-loader@2:
version "2.0.0"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c"
integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==
dependencies:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
style-search@^0.1.0: style-search@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902"