Skip to content

Commit a0d8200

Browse files
authored
Dev server middleware vite (#1225)
1 parent eedaa2d commit a0d8200

23 files changed

+259
-58
lines changed

.changeset/dark-wolves-argue.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'sku': minor
3+
---
4+
5+
`vite start`: Enable `devServerMiddleware` for vite projects

docs/docs/vite-support.md

+25
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,31 @@ export default () => (
6060
);
6161
```
6262

63+
### Dev server middleware
64+
65+
The `vite` dev server runs on a [`Connect`](https://github.com/senchalabs/connect) instance instead of [`Express`](https://expressjs.com/) so the middleware setup is slightly different. A middleware file still exports a single function however it now takes the [`ViteDevServer`](https://vite.dev/guide/api-javascript.html#vitedevserver) as the only parameter.
66+
To add middleware to the dev server you should follow the connect guide [here](https://github.com/senchalabs/connect#use-middleware).
67+
68+
**Example of using `devServerMiddleware` with `vite`**
69+
70+
```typescript
71+
// custom-middleware.ts
72+
import type { ViteDevServer } from 'vite';
73+
74+
export default function (server: ViteDevServer) {
75+
server.middlewares.use((req, res, next) => {
76+
// your middleware logic
77+
next();
78+
});
79+
80+
// or use a path
81+
server.middlewares.use('/api', (req, res, next) => {
82+
// your middleware logic
83+
next();
84+
});
85+
}
86+
```
87+
6388
### Using vite types
6489

6590
If [`vite/client` types](https://vite.dev/guide/features#client-types) are needed in your project, you can reference them via the `sku/vite/client` entrypoint:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default (app) => {
2+
app.middlewares.use('/test-middleware', (req, res, next) => {
3+
if (req.method !== 'GET') {
4+
next();
5+
}
6+
res.end('OK');
7+
});
8+
};

fixtures/sku-with-https/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "@sku-fixtures/sku-with-https",
33
"private": true,
4+
"type": "module",
45
"dependencies": {
56
"react": "^18.2.0",
67
"react-dom": "^18.2.0"

fixtures/sku-with-https/sku-server.config.mjs

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import {
44
} from '@sku-private/test-utils';
55

66
export default {
7-
clientEntry: 'src/serverClient.js',
7+
clientEntry: 'src/serverClient.jsx',
8+
serverEntry: 'src/server.jsx',
9+
renderEntry: 'src/render.jsx',
810
port: 9843,
911
serverPort: 9894,
1012
httpsDevServer: true,
11-
devServerMiddleware: './dev-middleware.js',
13+
devServerMiddleware: './dev-middleware.cjs',
1214
cspEnabled: true,
1315
cspExtraScriptSrcHosts: ['https://some-cdn.com'],
1416
dangerouslySetWebpackConfig: (config) => {

fixtures/sku-with-https/sku.config.mjs

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import {
66
export default {
77
port: 9843,
88
httpsDevServer: true,
9-
devServerMiddleware: './dev-middleware.js',
9+
clientEntry: './src/client.jsx',
10+
renderEntry: './src/render.jsx',
11+
serverEntry: './src/server.jsx',
12+
devServerMiddleware: './dev-middleware.cjs',
1013
cspEnabled: true,
1114
cspExtraScriptSrcHosts: ['https://some-cdn.com'],
1215
dangerouslySetWebpackConfig: (config) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import baseConfig from './sku.config.mjs';
2+
3+
export default {
4+
...baseConfig,
5+
__UNSAFE_EXPERIMENTAL__bundler: 'vite',
6+
httpsDevServer: false, // not yet supported
7+
devServerMiddleware: './dev-middleware.vite.js',
8+
};

fixtures/translations/.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
.eslintcache
66
.prettierrc
77
coverage/
8-
dist/
8+
dist-ssr/
99
eslint.config.mjs
1010
report/
1111
tsconfig.json

fixtures/translations/.prettierignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ translations.ts
55
.eslintcache
66
.prettierrc
77
coverage/
8-
dist/
8+
dist-ssr/
99
eslint.config.mjs
1010
pnpm-lock.yaml
1111
report/

packages/sku/src/program/commands/start-ssr/webpack-start-ssr-handler.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { watchVocabCompile } from '@/services/vocab/runVocab.js';
2727
import { configureProject, validatePeerDeps } from '@/utils/configure.js';
2828
import type { StatsChoices } from '../../options/stats/stats.option.js';
2929
import type { SkuContext } from '@/context/createSkuContext.js';
30+
import { requireFromCwd } from '@/utils/cwd.js';
3031

3132
const log = debug('sku:start-ssr');
3233

@@ -58,6 +59,7 @@ export const webpackStartSsrHandler = async ({
5859
}) => {
5960
process.env.NODE_ENV = 'development';
6061
const { port, initialPath, paths, httpsDevServer, hosts } = skuContext;
62+
const { type } = requireFromCwd('./package.json');
6163
await configureProject(skuContext);
6264
validatePeerDeps(skuContext);
6365
await watchVocabCompile(skuContext);
@@ -95,7 +97,7 @@ export const webpackStartSsrHandler = async ({
9597
const serverCompiler = webpack(serverWebpackConfig);
9698

9799
const serverManager = createServerManager(
98-
path.join(paths.target, 'server.js'),
100+
path.join(paths.target, `server.${type === 'module' ? 'c' : ''}js`),
99101
);
100102

101103
const proto = httpsDevServer ? 'https' : 'http';

packages/sku/src/services/vite/plugins/skuViteMiddlewarePlugin.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,24 @@ const clientEntry = require.resolve('../entries/vite-client.js');
1919

2020
export const skuViteMiddlewarePlugin = (skuContext: SkuContext): Plugin => ({
2121
name: 'vite-plugin-sku-server-middleware',
22-
configureServer(server) {
22+
async configureServer(server) {
2323
if (metricsMeasurers.initialPageLoad.isInitialPageLoad) {
2424
metricsMeasurers.initialPageLoad.mark();
2525
}
26+
if (skuContext.useDevServerMiddleware) {
27+
log(
28+
'Using dev server middleware at %s',
29+
skuContext.paths.devServerMiddleware,
30+
);
31+
const devServerMiddleware = (
32+
await import(skuContext.paths.devServerMiddleware)
33+
).default;
34+
if (devServerMiddleware && typeof devServerMiddleware === 'function') {
35+
devServerMiddleware(server);
36+
log('Dev server middleware loaded');
37+
}
38+
}
39+
2640
log('Configuring server middleware');
2741
server.middlewares.use(async (req, res, next) => {
2842
log('Handling request:', req.url);
@@ -68,7 +82,7 @@ export const skuViteMiddlewarePlugin = (skuContext: SkuContext): Plugin => ({
6882
language,
6983
route: getRouteWithLanguage(matchingRoute.route, language),
7084
routeName: matchingRoute.name || '',
71-
site: site.name,
85+
site: site?.name || '',
7286
clientEntry,
7387
});
7488

packages/sku/src/services/webpack/config/webpack.config.ssr.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { VocabWebpackPlugin } from '@vocab/webpack';
1515

1616
import { bundleAnalyzerPlugin } from './plugins/bundleAnalyzer.js';
1717
import { JAVASCRIPT, resolvePackage } from './utils/index.js';
18-
import { cwd } from '@/utils/cwd.js';
18+
import { cwd, requireFromCwd } from '@/utils/cwd.js';
1919
import { getVocabConfig } from '@/services/vocab/config/vocab.js';
2020
import getStatsConfig from './statsConfig.js';
2121
import getSourceMapSetting from './sourceMaps.js';
@@ -81,6 +81,8 @@ const makeWebpackConfig = ({
8181
// non-deterministic snapshots in jest tests.
8282
const fileMask = isDevServer ? '[name]' : '[name]-[contenthash]';
8383

84+
const { type } = requireFromCwd('./package.json');
85+
8486
return [
8587
{
8688
name: 'client',
@@ -253,7 +255,7 @@ const makeWebpackConfig = ({
253255
output: {
254256
path: paths.target,
255257
publicPath,
256-
filename: 'server.js',
258+
filename: `server.${type === 'module' ? 'c' : ''}js`,
257259
library: { name: 'server', type: 'var' },
258260
},
259261
cache: getCacheSettings({ isDevServer, paths }),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const getServerEntry = () => {};

test-utils/appSnapshot.js renamed to test-utils/appSnapshot.ts

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
// @ts-check
2-
/**
3-
* @param {string} url
4-
*/
5-
export const getAppSnapshot = async (url, warningFilter = () => true) => {
6-
/** @type {string[]} */
7-
const warnings = [];
8-
/** @type {string[]} */
9-
const errors = [];
1+
export const getAppSnapshot = async (
2+
url: string,
3+
warningFilter = () => true,
4+
) => {
5+
const warnings: string[] = [];
6+
const errors: string[] = [];
107

118
const appPage = await browser.newPage();
129

@@ -36,11 +33,15 @@ export const getAppSnapshot = async (url, warningFilter = () => true) => {
3633
});
3734

3835
const response = await appPage.goto(url, { waitUntil: 'load' });
39-
const sourceHtml = await response?.text();
40-
const clientRenderContent = await appPage.content();
36+
const sourceHtml = sanitizeHtml((await response?.text()) || '');
37+
const clientRenderContent = sanitizeHtml(await appPage.content());
4138

4239
expect(warnings).toEqual([]);
4340
expect(errors).toEqual([]);
4441

4542
return { sourceHtml, clientRenderContent };
4643
};
44+
45+
function sanitizeHtml(str: string) {
46+
return str.replaceAll(process.cwd(), '{cwd}');
47+
}

test-utils/waitForUrls.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const waitForUrls = async (...urls) => {
1515
headers: { accept: 'text/html, application/javascript' },
1616
timeout,
1717
// Log output of wait behaviour timing to allow
18-
// increased debugging when service fails to start
18+
// increased debugging when service fails to start
1919
log: false,
2020
strictSSL: false,
2121
});

0 commit comments

Comments
 (0)