Skip to content

Commit

Permalink
Merge pull request #10194 from guardian/mxdvl/webpack-dev-server
Browse files Browse the repository at this point in the history
Port the `devServer` to the new Webpack config
  • Loading branch information
mxdvl authored Jan 15, 2024
2 parents 1a62e21 + 686c28a commit 023c41c
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 9 deletions.
4 changes: 3 additions & 1 deletion dotcom-rendering/configs/webpack/.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import newWeb from './client.web.mjs';
import newWebLegacy from './client.web.legacy.mjs';
import newApps from './client.apps.mjs';
import newServer from './server.mjs';
import newDevServer from './server.dev.mjs';
import { isProd } from './utils/env.mjs';

const [server, web, webLegacy, apps] = current;

Expand Down Expand Up @@ -47,4 +49,4 @@ const compareConfigs = (current, proposed) => {
compareConfigs(web, newWeb);
compareConfigs(webLegacy, newWebLegacy);
compareConfigs(apps, newApps);
compareConfigs(server, newServer);
compareConfigs(server, isProd ? newServer : newDevServer);
100 changes: 98 additions & 2 deletions dotcom-rendering/configs/webpack/server.dev.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,114 @@
// THIS IS ONE IS TRICKIER, THIS IS MORE OF A GUIDE FOR HOW TO PROCEED.
// IT'S NOT FINISHED.

import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import bodyParser from 'body-parser';
// eslint-disable-next-line import/no-named-as-default -- this is the Webpack way
import webpack from 'webpack';
import webpackHotServerMiddleware from 'webpack-hot-server-middleware';
import { mergeWithRules } from 'webpack-merge';
import nodeExternals from 'webpack-node-externals';
import { success } from '../../../scripts/log.js';
import { getContentFromURLMiddleware } from '../../src/server/lib/get-content-from-url.js';
import { base } from './base.mjs';
import { server } from './server.mjs';

const DIRNAME = fileURLToPath(new URL('.', import.meta.url));

/** @type {import("webpack").Configuration} */
export const devServer = {
devServer: {
port: 3030,
compress: false,
hot: false,
liveReload: true,
client: {
logging: 'warn',
overlay: true,
},
static: {
directory: resolve(DIRNAME, '..', '..', 'src', 'static'),
publicPath: '/static/frontend',
},
allowedHosts: ['r.thegulocal.com'],
devMiddleware: {
publicPath: '/assets/',
serverSideRender: true,
headers: (req, res) => {
// Allow any localhost request from accessing the assets
if (
req.hostname === (process.env.HOSTNAME || 'localhost') &&
req.headers.origin
)
res.setHeader(
'Access-Control-Allow-Origin',
req.headers.origin,
);
},
},
setupMiddlewares: (middlewares, { app, compiler }) => {
if (!app) {
throw new Error('webpack-dev-server is not defined');
}

// it turns out webpack dev server is just an express server
// with webpack-dev-middleware, so here we add some other middlewares
// of our own

app.use(bodyParser.json({ limit: '10mb' }));

// populates req.body with the content data from a production
// URL if req.params.url is present
app.use(getContentFromURLMiddleware);

app.get('/', (req, res) => {
res.sendFile(
resolve(
DIRNAME,
'..',
'..',
'src',
'server',
'dev-index.html',
),
);
});

// webpack-hot-server-middleware needs to run after webpack-dev-middleware
if (compiler instanceof webpack.MultiCompiler) {
middlewares.push({
name: 'server',
middleware: webpackHotServerMiddleware(compiler, {
chunkName: 'server',
}),
});
}

return middlewares;
},
onListening: ({ options: { port } }) => {
if (typeof port !== 'number') return;
success(
[
'DEV server running on ',
'\x1b[36m', // cyan
'\x1b[4m', // underline
`http://localhost:${port}`,
'\x1b[0m', // reset
].join(''),
);
},
},
externals: [
// https://github.com/liady/webpack-node-externals/issues/105

nodeExternals({
allowlist: [/^@guardian/],
allowlist: [
/^@guardian/,
// this project is ESM-only and throws an error when not bundled
'screenfull',
],
additionalModuleDirs: [
// Since we use yarn-workspaces for the monorepo, node_modules
// will be co-located both in the
Expand Down Expand Up @@ -66,7 +162,7 @@ const merge = mergeWithRules({
module: {
rules: {
test: 'match',
use: [{ loading: 'match', options: 'merge' }],
use: [{ loader: 'match', options: 'merge' }],
},
},
});
Expand Down
17 changes: 11 additions & 6 deletions dotcom-rendering/webpack.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
import { mkdir, readdir, writeFile } from 'node:fs/promises';
import { basename, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import devServer from './configs/webpack/server.dev.mjs';
import prodServer from './configs/webpack/server.mjs';
import { isDev } from './configs/webpack/utils/env.mjs';

const dirname = fileURLToPath(new URL('.', import.meta.url));

const configsDirectory = resolve(dirname, 'configs', 'webpack');

const bundleConfigs = await readdir(configsDirectory).then((files) =>
files.filter((file) => file.startsWith('bundle')),
const clientConfigs = await readdir(configsDirectory).then((files) =>
files.filter((file) => file.startsWith('client.')),
);

// Create a bundles manifest for the TypesScript compiler so we get type safely
Expand All @@ -37,13 +40,15 @@ await writeFile(
/** generated from webpack.config.js */
export const bundles = /** @type {const} @satisfies {readonly string[]} */ ([
\t${bundleConfigs.map((name) => `"${basename(name, '.mjs')}"`).join(',\n\t')},
\t${clientConfigs.map((name) => `"${basename(name, '.mjs')}"`).join(',\n\t')},
]);
`,
'utf-8',
);

export default bundleConfigs.map((name) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return -- can't know what we are importing
import(`${configsDirectory}/${name}`).then((config) => config.default),
export default [isDev ? devServer : prodServer].concat(
clientConfigs.map((name) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return -- can't know what we are importing
import(`${configsDirectory}/${name}`).then((config) => config.default),
),
);

0 comments on commit 023c41c

Please sign in to comment.