Skip to content

Instantly share code, notes, and snippets.

@cdeutsch
Created April 9, 2018 14:24
Show Gist options
  • Save cdeutsch/29d47db9fd91b0eafdc6d5b5392af46a to your computer and use it in GitHub Desktop.
Save cdeutsch/29d47db9fd91b0eafdc6d5b5392af46a to your computer and use it in GitHub Desktop.
Typescript Webpack, Node Server backend
import { Express } from 'express';
import path from 'path';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
const webpackConfig = require('../../../frontend/webpack/webpack.dev.config');
export function addDevMiddlewares(app: Express) {
const compiler = webpack(webpackConfig);
const middleware = webpackDevMiddleware(compiler, {
//noInfo: true,
publicPath: webpackConfig.output.publicPath,
//silent: true,
//stats: 'errors-only',
stats: { colors: true },
});
app.use(middleware);
// Since webpackDevMiddleware uses memory-fs internally to store build
// artifacts, we use it instead
const fs = middleware.fileSystem;
app.get('*', (req, res) => {
fs.readFile(path.join((compiler as any).outputPath, 'index.html'), {}, (err: Error, file: any) => {
if (err) {
console.error('Failed to load index.html', err);
res.sendStatus(404);
} else {
res.send(file.toString());
}
});
});
}
import compression from 'compression';
import express, { Express } from 'express';
import path from 'path';
export interface ProdMiddlewareOptions {
outputPath: string;
publicPath: string;
}
export function addProdMiddlewares(app: Express, options: ProdMiddlewareOptions) {
const publicPath = options.publicPath || '/';
const outputPath = options.outputPath || path.resolve(process.cwd(), '../../../frontend/dist');
// compression middleware compresses your server responses which makes them
// smaller (applies also to assets). You can read more about that technique
// and other good practices on official Express.js docs http://mxs.is/googmy
app.use(compression());
app.use(publicPath, express.static(outputPath));
app.get('*', (req, res) => {
res.sendFile(path.resolve(outputPath, 'index.html'));
});
}
import { Express } from 'express';
import { addDevMiddlewares } from './addDevMiddlewares';
import { addProdMiddlewares, ProdMiddlewareOptions } from './addProdMiddlewares';
export function frontendMiddleware(app: Express, options: ProdMiddlewareOptions) {
const isProd = process.env.NODE_ENV === 'production';
if (isProd) {
addProdMiddlewares(app, options);
} else {
addDevMiddlewares(app);
}
return app;
}
require('dotenv').config();
import express, { NextFunction, Request, Response } from 'express';
import { resolve } from 'path';
import { db } from './api/db';
const nodeCleanup = require('node-cleanup');
// This setups error handling when using async/await in express routes.
// Just throw an error like you naturally would.
require('express-async-errors');
import { frontendMiddleware } from './frontendMiddleware';
import { argv, logger, port } from './util';
import api from './api';
const app = express();
app.use('/api', api);
// In production we need to pass these values in instead of relying on webpack.
frontendMiddleware(app, {
outputPath: resolve(process.cwd(), '../../frontend/dist'),
publicPath: '/',
});
// get the intended host and port number, use localhost and port 3000 if not provided
const customHost = argv.host || process.env.HOST;
const host = customHost || null; // Let http.Server use its default IPv6/4 host
const prettyHost = customHost || 'localhost';
// // serve public files
// app.use(
// express.static(resolve(process.cwd(), 'public'), {
// index: 'index.html',
// })
// );
// Error handler for our async/await routes
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
if (err && err.message) {
res.status(500);
res.json({ error: err.message });
}
next(err);
});
// Start your app.
app.listen(port, host, (err: Error) => {
if (err) {
logger.error(err.message);
return;
}
logger.appStarted(port, prettyHost);
});
nodeCleanup((exitCode: number, signal: string) => {
console.debug('CLEANUP', exitCode, signal);
db.$pool.end();
});
// cSpell:ignore devtool
'use strict';
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
const srcPath = path.join(__dirname, '../src');
module.exports = (options) => ({
mode: options.mode,
devtool: options.devtool,
entry: {
app: path.join(srcPath, 'index.tsx'),
},
output: Object.assign({ // Compile into dist/[name].js
path: path.resolve(process.cwd(), 'dist'),
publicPath: '/',
}, options.output), // Merge with env dependent settings
resolve: {
extensions: ['.ts', '.tsx', '.js'],
alias: {
'@root': srcPath,
},
},
devServer: {
contentBase: path.resolve(process.cwd(), 'dist'),
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader',
options: {
configFile: 'tsconfig.json',
transpileOnly: true, // disable type checker because we will type check using fork-ts-checker-webpack-plugin
}
},
],
},
],
},
plugins: options.plugins.concat([
new ProgressBarPlugin(),
new CleanWebpackPlugin(['dist'], {
root: process.cwd(),
}),
// Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
// inside your code for any environment checks; UglifyJS will automatically
// drop any unreachable code.
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
MOBX_DEV_TOOLS: JSON.stringify(process.env.MOBX_DEV_TOOLS || 'false'),
},
}),
new ForkTsCheckerWebpackPlugin(),
new WebpackBuildNotifierPlugin({
title: "The Inspector",
logo: path.resolve("../notifier.png"),
suppressSuccess: true
}),
]),
target: 'web', // Make web variables accessible to webpack, e.g. window
performance: options.performance || {},
optimization: {
minimize: false,
runtimeChunk: {
name: 'vendor'
},
splitChunks: {
cacheGroups: {
default: false,
commons: {
test: /node_modules/,
name: "vendor",
chunks: "initial",
minSize: 1
},
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
}
});
/**
* DEVELOPMENT WEBPACK CONFIGURATION
*/
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// const CircularDependencyPlugin = require('circular-dependency-plugin');
module.exports = require('./webpack.base.config')({
mode: 'development',
// Don't use hashes in dev mode for better performance
output: {
filename: '[name].js',
chunkFilename: '[name].chunk.js'
},
// Add development plugins
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(process.cwd(), '../frontend/public/index.html'),
title: 'The Inspector',
filename: 'index.html',
inject: true,
}),
// new CircularDependencyPlugin({
// exclude: /a\.js|node_modules/, // exclude node_modules
// failOnError: false // show a warning when there is a circular dependency
// })
],
// Emit a source map for easier debugging
// See https://webpack.js.org/configuration/devtool/#devtool
devtool: 'eval-source-map',
performance: {
hints: false
}
});
// Important modules this config uses
const path = require('path');
// const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = require('./webpack.base.config')({
mode: 'production',
// Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
output: {
filename: '[name].[chunkhash:6].js',
chunkFilename: 'chunks/[name].[chunkhash:6].js',
},
plugins: [
// Minify and optimize the index.html
new HtmlWebpackPlugin({
template: path.resolve(process.cwd(), 'public/index.html'),
title: 'The Inspector',
filename: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new OptimizeCSSAssetsPlugin({}),
],
performance: {
assetFilter: (assetFilename) => !(/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename)),
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment