Skip to content

Commit

Permalink
feat: add preview cmd (#12)
Browse files Browse the repository at this point in the history
* feat: add preview cmd

* docs: preview

* Update src/fishkit/server.ts

Co-authored-by: chencheng (云谦) <[email protected]>

* Update src/dev.ts

Co-authored-by: chencheng (云谦) <[email protected]>

* Update src/preview.ts

Co-authored-by: chencheng (云谦) <[email protected]>

* docs: preview cmd

* chore: update lock

* chore: code style

* chore: code style

* Update src/fishkit/server.ts

---------

Co-authored-by: chencheng (云谦) <[email protected]>
  • Loading branch information
xiaohuoni and sorrycc authored Nov 6, 2024
1 parent ca0d0a4 commit dcf4979
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 54 deletions.
5 changes: 5 additions & 0 deletions .changeset/silent-cherries-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@umijs/tnf': patch
---

feat: add preview cmd
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ $ pnpm dev
$ pnpm build
```

## Preview

```bash
# Preview the product after building the project
$ pnpm preview
```

## Test

```bash
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ $ npx serve dist -s
- `tnf create <project-name> --template=<template-name>`: Create a new project with the given template.
- `tnf build`: Build the project.
- `tnf dev`: Start the development server.
- `tnf preview`: Preview the product after building the project.

## API

Expand Down
2 changes: 1 addition & 1 deletion examples/hackernews/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"scripts": {
"build": "tnf build",
"dev": "tnf dev",
"preview": "npx serve dist -s"
"preview": "tnf preview"
},
"dependencies": {
"@types/react": "^18.3.12",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"picocolors": "^1.1.1",
"react": "19.0.0-rc-02c0e824-20241028",
"react-dom": "19.0.0-rc-02c0e824-20241028",
"sirv": "^3.0.0",
"spdy": "^4.0.2",
"yargs-parser": "^21.1.1",
"zod": "^3.23.8"
Expand Down
26 changes: 26 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ async function run(cmd: string, options: RunOptions) {
cwd: options.cwd,
config: await loadConfig({ cwd: options.cwd }),
});
case 'preview':
const { preview } = await import('./preview.js');
return preview({
cwd: options.cwd,
config: await loadConfig({ cwd: options.cwd }),
});
default:
throw new Error(`Unknown command: ${cmd}`);
}
Expand Down
14 changes: 1 addition & 13 deletions src/dev.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { BuildParams } from '@umijs/mako';
import { getPort } from 'get-port-please';
import { build } from './build';
import type { Config } from './config';
import { DEFAULT_PORT } from './constants';
import { createServer } from './fishkit/server';

export interface DevOpts {
Expand All @@ -12,17 +10,7 @@ export interface DevOpts {

export async function dev(opts: DevOpts) {
const devServer = opts.config?.devServer || {};
const port = await getPort(devServer.port || DEFAULT_PORT);
const hmrPort = await getPort(port + 1);
const host = devServer.host || 'localhost';

await createServer({
port,
hmrPort,
host,
https: devServer.https,
ip: devServer.ip,
});
const { hmrPort, host } = await createServer({ devServer, hmr: true });

// build mako config
let devMakoConfig: BuildParams['config'] = {};
Expand Down
90 changes: 50 additions & 40 deletions src/fishkit/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@ import history from 'connect-history-api-fallback';
import cors from 'cors';
import express from 'express';
import proxy from 'express-http-proxy';
import { getPort } from 'get-port-please';
import http from 'http';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { type Config } from '../config';
import { DEFAULT_PORT } from '../constants';
import { createHttpsServer } from './https';

export interface ServerOpts {
port: number;
hmrPort: number;
host: string;
https?: {
hosts?: string[];
};
ip?: string;
devServer: Config['devServer'];
hmr?: boolean;
}

export async function createServer(opts: ServerOpts) {
const {
port = DEFAULT_PORT,
host = 'localhost',
https,
ip,
} = opts.devServer || {};
const _port = await getPort(port);
const hmrPort = opts.hmr ? await getPort(_port + 1) : 0;
const app = express();

// cors
Expand All @@ -32,27 +38,30 @@ export async function createServer(opts: ServerOpts) {
// compression
app.use(compression());

// proxy ws to mako server
const wsProxy = createProxyMiddleware({
target: `http://127.0.0.1:${opts.hmrPort}`,
ws: true,
});
app.use('/__/hmr-ws', wsProxy);
let wsProxy;
if (opts.hmr) {
// proxy ws to mako server
wsProxy = createProxyMiddleware({
target: `http://127.0.0.1:${hmrPort}`,
ws: true,
});
app.use('/__/hmr-ws', wsProxy);

app.use(
proxy(`http://127.0.0.1:${opts.hmrPort}`, {
proxyReqOptDecorator: function (proxyReqOpts: any) {
proxyReqOpts.agent = false;
return proxyReqOpts;
},
filter: function (req: any, res: any) {
return req.method == 'GET' || req.method == 'HEAD';
},
skipToNextHandlerFilter: function (proxyRes: any) {
return proxyRes.statusCode !== 200;
},
}),
);
app.use(
proxy(`http://127.0.0.1:${hmrPort}`, {
proxyReqOptDecorator: function (proxyReqOpts: any) {
proxyReqOpts.agent = false;
return proxyReqOpts;
},
filter: function (req: any, res: any) {
return req.method == 'GET' || req.method == 'HEAD';
},
skipToNextHandlerFilter: function (proxyRes: any) {
return proxyRes.statusCode !== 200;
},
}),
);
}

// history fallback
app.use(
Expand All @@ -63,32 +72,33 @@ export async function createServer(opts: ServerOpts) {

// create server
let server;
const httpsOpts = opts.https;
if (httpsOpts) {
httpsOpts.hosts ||= uniq(
if (https) {
https.hosts ||= uniq(
[
...(httpsOpts.hosts || []),
...(https.hosts || []),
// always add localhost, 127.0.0.1, ip and host
'127.0.0.1',
'localhost',
opts.ip,
opts.host !== '0.0.0.0' && opts.host,
ip,
host !== '0.0.0.0' && host,
].filter(Boolean) as string[],
);
server = await createHttpsServer(app, httpsOpts);
server = await createHttpsServer(app, https);
} else {
server = http.createServer(app);
}

server.listen(opts.port, () => {
const protocol = opts.https ? 'https:' : 'http:';
console.log(`Server is running on ${protocol}//${opts.host}:${opts.port}`);
server.listen(_port, () => {
const protocol = https ? 'https:' : 'http:';
console.log(`Server is running on ${protocol}//${host}:${_port}`);
});

// prevent first websocket auto disconnected
server.on('upgrade', wsProxy.upgrade);
if (opts.hmr) {
// prevent first websocket auto disconnected
server.on('upgrade', wsProxy!.upgrade);
}

return server;
return { server, app, hmrPort, port: _port, ip, host };
}

function uniq(arr: string[]) {
Expand Down
24 changes: 24 additions & 0 deletions src/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { resolve } from 'path';
import sirv from 'sirv';
import type { Config } from './config';
import { createServer } from './fishkit/server';

export interface PreviewOpts {
cwd: string;
config?: Config;
}

export async function preview(opts: PreviewOpts) {
const devServer = opts.config?.devServer || {};
const { app } = await createServer({ devServer });
const distDir = resolve(opts.cwd, 'dist');

app.use(
'/',
sirv(distDir, {
etag: true,
dev: true,
single: true,
}),
);
}

0 comments on commit dcf4979

Please sign in to comment.