Make the webpack config work for us

This moves the babel and postcss configs into the webpack config for ease of maintenance (and because we need variations of them). The typescript config is left outside the webpack config for IDEs to pick it up.
This commit is contained in:
Travis Ralston 2019-12-23 19:48:19 -07:00
parent c44a6e296e
commit 1b41dc3291
6 changed files with 2539 additions and 1304 deletions

View File

@ -1,20 +0,0 @@
{
"presets": [
"react",
"es2015",
"es2016"
],
"plugins": [
[
"transform-builtin-extend",
{
"globals": ["Error"]
}
],
"transform-class-properties",
"transform-object-rest-spread",
"transform-runtime",
"add-module-exports",
"syntax-dynamic-import"
]
}

View File

@ -63,8 +63,6 @@
"test-multi": "karma start" "test-multi": "karma start"
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "^6.26.0",
"babel-runtime": "^6.26.0",
"browser-request": "^0.3.3", "browser-request": "^0.3.3",
"favico.js": "^0.3.10", "favico.js": "^0.3.10",
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566", "gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
@ -83,27 +81,32 @@
"url": "^0.11.0" "url": "^0.11.0"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "^6.6.0", "@babel/cli": "^7.7.5",
"babel-cli": "^6.26.0", "@babel/core": "^7.7.5",
"babel-core": "^6.26.3", "@babel/plugin-proposal-class-properties": "^7.7.4",
"babel-eslint": "^8.1.1", "@babel/plugin-proposal-decorators": "^7.7.4",
"babel-loader": "^7.1.5", "@babel/plugin-proposal-export-default-from": "^7.7.4",
"babel-plugin-add-module-exports": "^0.2.1", "@babel/plugin-proposal-numeric-separator": "^7.7.4",
"babel-plugin-syntax-dynamic-import": "^6.18.0", "@babel/plugin-proposal-object-rest-spread": "^7.7.4",
"babel-plugin-transform-builtin-extend": "^1.1.2", "@babel/plugin-syntax-dynamic-import": "^7.7.4",
"babel-plugin-transform-class-properties": "^6.24.1", "@babel/plugin-transform-flow-comments": "^7.7.4",
"babel-plugin-transform-object-rest-spread": "^6.26.0", "@babel/plugin-transform-runtime": "^7.7.6",
"babel-plugin-transform-runtime": "^6.23.0", "@babel/preset-env": "^7.7.6",
"babel-preset-es2015": "^6.24.1", "@babel/preset-flow": "^7.7.4",
"babel-preset-es2016": "^6.24.1", "@babel/preset-react": "^7.7.4",
"babel-preset-es2017": "^6.24.1", "@babel/preset-typescript": "^7.7.4",
"babel-preset-react": "^6.24.1", "@babel/register": "^7.7.4",
"babel-preset-stage-2": "^6.24.1", "@babel/runtime": "^7.7.6",
"@types/react": "^16.9.16",
"@types/react-dom": "^16.9.4",
"autoprefixer": "^9.7.3",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"chokidar": "^2.0.4", "chokidar": "^2.0.4",
"concurrently": "^4.0.1", "concurrently": "^4.0.1",
"cpx": "^1.3.2", "cpx": "^1.3.2",
"cross-env": "^4.0.0", "cross-env": "^4.0.0",
"css-loader": "^2.1.0", "css-loader": "^3.3.2",
"electron-builder": "^21.2.0", "electron-builder": "^21.2.0",
"electron-builder-squirrel-windows": "^21.2.0", "electron-builder-squirrel-windows": "^21.2.0",
"electron-devtools-installer": "^2.2.4", "electron-devtools-installer": "^2.2.4",
@ -117,7 +120,7 @@
"eslint-plugin-react-hooks": "^2.2.0", "eslint-plugin-react-hooks": "^2.2.0",
"expect": "^1.16.0", "expect": "^1.16.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0", "extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^3.0.1", "file-loader": "^5.0.2",
"fs-extra": "^0.30.0", "fs-extra": "^0.30.0",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"json-loader": "^0.5.3", "json-loader": "^0.5.3",
@ -133,23 +136,27 @@
"loader-utils": "^1.2.3", "loader-utils": "^1.2.3",
"matrix-mock-request": "^1.2.3", "matrix-mock-request": "^1.2.3",
"matrix-react-test-utils": "^0.2.2", "matrix-react-test-utils": "^0.2.2",
"mini-css-extract-plugin": "^0.8.0",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-extend": "^1.0.5", "postcss-extend": "^1.0.5",
"postcss-import": "^11.1.0", "postcss-import": "^12.0.1",
"postcss-loader": "^2.1.6", "postcss-loader": "^3.0.0",
"postcss-mixins": "^6.2.0", "postcss-mixins": "^6.2.3",
"postcss-nested": "^3.0.0", "postcss-nested": "^4.2.1",
"postcss-scss": "^1.0.6", "postcss-preset-env": "^6.7.0",
"postcss-simple-vars": "^4.1.0", "postcss-scss": "^2.0.0",
"postcss-simple-vars": "^5.0.2",
"postcss-strip-inline-comments": "^0.1.5", "postcss-strip-inline-comments": "^0.1.5",
"rimraf": "^2.4.3", "rimraf": "^2.4.3",
"shell-escape": "^0.2.0", "shell-escape": "^0.2.0",
"source-map-loader": "^0.2.4", "terser-webpack-plugin": "^2.3.0",
"webpack": "^4.23.1", "typescript": "^3.7.3",
"webpack-cli": "^3.1.2", "webpack": "^4.41.2",
"webpack-dev-server": "^3.1.11" "webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
}, },
"build": { "build": {
"appId": "im.riot.app", "appId": "im.riot.app",

View File

@ -1,14 +0,0 @@
module.exports = {
plugins: [
require("postcss-import")(),
require("autoprefixer")(),
require("postcss-simple-vars")(),
require("postcss-extend")(),
require("postcss-nested")(),
require("postcss-mixins")(),
require("postcss-easings")(),
require("postcss-strip-inline-comments")(),
],
"parser": "postcss-scss",
"local-plugins": true,
};

19
tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es2016",
"noImplicitAny": false,
"sourceMap": false,
"outDir": "./lib",
"declaration": true,
"types": [
"node"
]
},
"include": [
"./src/**/*.ts"
]
}

View File

@ -1,18 +1,17 @@
const path = require('path'); const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const webpack = require("webpack");
let og_image_url = process.env.RIOT_OG_IMAGE_URL; let og_image_url = process.env.RIOT_OG_IMAGE_URL;
if (!og_image_url) og_image_url = 'https://riot.im/app/themes/riot/img/logos/riot-im-logo-black-text.png'; if (!og_image_url) og_image_url = 'https://riot.im/app/themes/riot/img/logos/riot-im-logo-black-text.png';
module.exports = { module.exports = (env, argv) => ({
entry: { entry: {
// Load babel-polyfill first to avoid issues where some imports (namely react) "bundle": "./src/vector/index.js",
// are potentially loaded before babel-polyfill.
"bundle": ["babel-polyfill", "./src/vector/index.js"],
"indexeddb-worker": "./src/vector/indexeddb-worker.js", "indexeddb-worker": "./src/vector/indexeddb-worker.js",
"mobileguide": "./src/vector/mobile_guide/index.js", "mobileguide": "./src/vector/mobile_guide/index.js",
// CSS themes // CSS themes
@ -21,10 +20,196 @@ module.exports = {
"theme-light-custom": "./node_modules/matrix-react-sdk/res/themes/light-custom/css/light-custom.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", "theme-dark-custom": "./node_modules/matrix-react-sdk/res/themes/dark-custom/css/dark-custom.scss",
}, },
optimization: {
// Put all of our CSS into one useful place - this is needed for MiniCssExtractPlugin.
// Previously we used a different extraction plugin that did this magic for us, but
// now we need to consider that the CSS needs to be bundled up together.
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
enforce: true,
// Do not add `chunks: 'all'` here because you'll break the app entry point.
},
},
},
// Minification is normally enabled by default for webpack in production mode, but
// we use a CSS optimizer too and need to manage it ourselves.
minimize: argv.mode === 'production',
minimizer: argv.mode === 'production' ? [new TerserPlugin({}), new OptimizeCSSAssetsPlugin({})] : [],
},
resolve: {
// We define an alternative import path so we can safely use src/ across the react-sdk
// and js-sdk. We already import from src/ where possible to ensure our source maps are
// extremely accurate (and because we're capable of compiling the layers manually rather
// than relying on partially-mangled output from babel), though we do need to fix the
// package level import (stuff like `import {Thing} from "matrix-js-sdk"` for example).
// We can't use the aliasing down below to point at src/ because that'll fail to resolve
// the package.json for the dependency. Instead, we rely on the package.json of each
// layer to have our custom alternate fields to load things in the right order. These are
// the defaults of webpack prepended with `matrix_src_`.
mainFields: ['matrix_src_browser', 'matrix_src_main', 'browser', 'main'],
aliasFields: ['matrix_src_browser', 'browser'],
// We need to ensure we can resolve TS files, but that also means we need to define
// every single extension we might see, ever.
extensions: [
'.js',
'.json',
'.css',
'.scss',
'.ts',
'.gif',
'.png',
'.svg',
'.ttf',
'.woff',
'.woff2',
'.xml',
'.ico',
'.wasm',
],
alias: {
// alias any requires to the react module to the one in our path,
// otherwise we tend to get the react source included twice when
// using `npm link` / `yarn link`.
"react": path.resolve(__dirname, 'node_modules/react'),
"react-dom": path.resolve(__dirname, 'node_modules/react-dom'),
// same goes for js-sdk - we don't need two copies.
"matrix-js-sdk": path.resolve(__dirname, 'node_modules/matrix-js-sdk'),
// Define a variable so the i18n stuff can load
"$webapp": path.resolve(__dirname, 'webapp'),
},
},
module: { module: {
noParse: [
// for cross platform compatibility use [\\\/] as the path separator
// this ensures that the regex trips on both Windows and *nix
// don't parse the languages within highlight.js. They cause stack
// overflows (https://github.com/webpack/webpack/issues/1721), and
// there is no need for webpack to parse them - they can just be
// included as-is.
/highlight\.js[\\\/]lib[\\\/]languages/,
// olm takes ages for webpack to process, and it's already heavily
// optimised, so there is little to gain by us uglifying it.
/olm[\\\/](javascript[\\\/])?olm\.js$/,
],
rules: [ rules: [
{ enforce: 'pre', test: /\.js$/, use: "source-map-loader", exclude: /node_modules/, }, {
{ test: /\.js$/, use: "babel-loader", include: path.resolve(__dirname, 'src') }, test: /\.(ts|js)x?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// We have the babel config here rather than a file to help
// make it clearer what we're compiling to.
cacheDirectory: true,
sourceMaps: true,
presets: [
["@babel/preset-env", {
"targets": {
"browsers": [
"last 2 versions",
]
},
}],
"@babel/preset-typescript",
"@babel/preset-flow",
"@babel/preset-react",
],
plugins: [
// Most of these plugins are for the react-sdk to make itself
// work correctly.
["@babel/plugin-proposal-decorators", {"legacy": true}],
"@babel/plugin-proposal-export-default-from",
"@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-transform-flow-comments",
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-transform-runtime"
],
}
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
sourceMap: true,
}
},
{
loader: 'postcss-loader',
ident: 'postcss',
options: {
sourceMap: true,
plugins: () => [
// Note that we use significantly fewer plugins on the plain
// CSS parser. If we start to parse plain CSS, we end with all
// kinds of nasty problems (like stylesheets not loading).
require("postcss-simple-vars")(),
require("postcss-strip-inline-comments")(),
// It's important that this plugin is last otherwise we end
// up with broken CSS.
require('postcss-preset-env')({stage: 3, browsers: 'last 2 versions'}),
],
parser: "postcss-scss",
"local-plugins": true,
},
},
]
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
sourceMap: true,
}
},
{
loader: 'postcss-loader',
ident: 'postcss',
options: {
sourceMap: true,
plugins: () => [
// Note that we use slightly different plugins for SCSS.
require('postcss-import')(),
require("postcss-simple-vars")(),
require("postcss-extend")(),
require("postcss-nested")(),
require("postcss-mixins")(),
require("postcss-easings")(),
require("postcss-strip-inline-comments")(),
// It's important that this plugin is last otherwise we end
// up with broken CSS.
require('postcss-preset-env')({stage: 3, browsers: 'last 2 versions'}),
],
parser: "postcss-scss",
"local-plugins": true,
},
},
]
},
{ {
test: /\.wasm$/, test: /\.wasm$/,
loader: "file-loader", loader: "file-loader",
@ -34,34 +219,6 @@ module.exports = {
outputPath: '.', outputPath: '.',
}, },
}, },
{
test: /\.scss$/,
// 1. postcss-loader turns the SCSS into normal CSS.
// 2. css-loader turns the CSS into a JS module whose default
// export is a string containing the CSS, while also adding
// the images and fonts from CSS as Webpack inputs.
// 3. ExtractTextPlugin turns that string into a separate asset.
use: ExtractTextPlugin.extract({
use: [
"css-loader",
{
loader: 'postcss-loader',
options: {
config: {
path: './postcss.config.js',
},
},
},
],
}),
},
{
// this works similarly to the scss case, without postcss.
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: "css-loader",
}),
},
{ {
// cache-bust languages.json file placed in // cache-bust languages.json file placed in
// riot-web/webapp/i18n during build by copy-res.js // riot-web/webapp/i18n during build by copy-res.js
@ -82,6 +239,7 @@ module.exports = {
issuer: /\.(scss|css)$/, issuer: /\.(scss|css)$/,
loader: 'file-loader', loader: 'file-loader',
options: { options: {
esModule: false,
name: '[name].[hash:7].[ext]', name: '[name].[hash:7].[ext]',
outputPath: getImgOutputPath, outputPath: getImgOutputPath,
publicPath: function(url, resourcePath) { publicPath: function(url, resourcePath) {
@ -97,6 +255,7 @@ module.exports = {
// Assets referenced in HTML and JS files // Assets referenced in HTML and JS files
loader: 'file-loader', loader: 'file-loader',
options: { options: {
esModule: false,
name: '[name].[hash:7].[ext]', name: '[name].[hash:7].[ext]',
outputPath: getImgOutputPath, outputPath: getImgOutputPath,
publicPath: function(url, resourcePath) { publicPath: function(url, resourcePath) {
@ -107,26 +266,50 @@ module.exports = {
}, },
], ],
}, },
], ]
noParse: [
// for cross platform compatibility use [\\\/] as the path separator
// this ensures that the regex trips on both Windows and *nix
// don't parse the languages within highlight.js. They cause stack
// overflows (https://github.com/webpack/webpack/issues/1721), and
// there is no need for webpack to parse them - they can just be
// included as-is.
/highlight\.js[\\\/]lib[\\\/]languages/,
// olm takes ages for webpack to process, and it's already heavily
// optimised, so there is little to gain by us uglifying it.
/olm[\\\/](javascript[\\\/])?olm\.js$/,
],
}, },
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
},
}),
// This exports our CSS using the splitChunks and loaders above.
new MiniCssExtractPlugin({
filename: 'bundles/[hash]/[name].css',
ignoreOrder: false, // Enable to remove warnings about conflicting order
}),
// This is the app's main entry point.
new HtmlWebpackPlugin({
template: './src/vector/index.html',
// we inject the links ourselves via the template, because
// HtmlWebpackPlugin will screw up our formatting like the names
// of the themes and which chunks we actually care about.
inject: false,
excludeChunks: ['mobileguide'],
minify: argv.mode === 'production',
vars: {
og_image_url: og_image_url,
},
}),
// This is the mobile guide's entry point (separate for faster mobile loading)
new HtmlWebpackPlugin({
template: './src/vector/mobile_guide/index.html',
filename: 'mobile_guide/index.html',
minify: argv.mode === 'production',
chunks: ['mobileguide'],
}),
],
output: { output: {
path: path.join(__dirname, "webapp"), path: path.join(__dirname, "webapp"),
// The generated JS (and CSS, from the ExtractTextPlugin) are put in a // The generated JS (and CSS, from the extraction plugin) are put in a
// unique subdirectory for the build. There will only be one such // unique subdirectory for the build. There will only be one such
// 'bundle' directory in the generated tarball; however, hosting // 'bundle' directory in the generated tarball; however, hosting
// servers can collect 'bundles' from multiple versions into one // servers can collect 'bundles' from multiple versions into one
@ -135,62 +318,12 @@ module.exports = {
// chunks even after the app is redeployed. // chunks even after the app is redeployed.
filename: "bundles/[hash]/[name].js", filename: "bundles/[hash]/[name].js",
chunkFilename: "bundles/[hash]/[name].js", chunkFilename: "bundles/[hash]/[name].js",
devtoolModuleFilenameTemplate: function(info) {
// Reading input source maps gives only relative paths here for
// everything. Until I figure out how to fix this, this is a
// workaround.
// We use the relative resource path with any '../'s on the front
// removed which gives a tree with matrix-react-sdk and vector
// trees smashed together, but this fixes everything being under
// various levels of '.' and '..'
// Also, sometimes the resource path is absolute.
return path.relative(process.cwd(), info.resourcePath).replace(/^[\/\.]*/, '');
},
}, },
resolve: {
alias: {
// alias any requires to the react module to the one in our path,
// otherwise we tend to get the react source included twice when
// using `npm link` / `yarn link`.
"react": path.resolve('./node_modules/react'),
"react-dom": path.resolve('./node_modules/react-dom'),
// same goes for js-sdk // DO NOT enable this option. It makes the source maps all wonky. Instead,
"matrix-js-sdk": path.resolve('./node_modules/matrix-js-sdk'), // we end up including the sourcemaps through the loaders which makes them
// more accurate.
"$webapp": path.resolve('./webapp'), //devtool: "source-map",
},
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
},
}),
new ExtractTextPlugin("bundles/[hash]/[name].css", {
allChunks: true,
}),
new HtmlWebpackPlugin({
template: './src/vector/index.html',
// we inject the links ourselves via the template, because
// HtmlWebpackPlugin wants to put the script tags either at the
// bottom of <head> or the bottom of <body>, and I'm a bit scared
// about moving them.
inject: false,
excludeChunks: ['mobileguide'],
vars: {
og_image_url: og_image_url,
},
}),
new HtmlWebpackPlugin({
template: './src/vector/mobile_guide/index.html',
filename: 'mobile_guide/index.html',
chunks: ['mobileguide'],
}),
],
devtool: 'source-map',
// configuration for the webpack-dev-server // configuration for the webpack-dev-server
devServer: { devServer: {
@ -202,13 +335,13 @@ module.exports = {
chunks: false, chunks: false,
}, },
// hot mdule replacement doesn't work (I think we'd need react-hot-reload?) // hot module replacement doesn't work (I think we'd need react-hot-reload?)
// so webpack-dev-server reloads the page on every update which is quite // so webpack-dev-server reloads the page on every update which is quite
// tedious in Riot since that can take a while. // tedious in Riot since that can take a while.
hot: false, hot: false,
inline: false, inline: false,
}, },
}; });
/** /**
* Merge assets found via CSS and imports into a single tree, while also preserving * Merge assets found via CSS and imports into a single tree, while also preserving

3372
yarn.lock

File diff suppressed because it is too large Load Diff