Skip to content

Commit

Permalink
feat: support next/nest
Browse files Browse the repository at this point in the history
  • Loading branch information
keyboard3 committed Sep 24, 2022
1 parent 2d0aef1 commit b2e82af
Show file tree
Hide file tree
Showing 17 changed files with 437 additions and 0 deletions.
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# compiled output
*/dist
dist
/node_modules
*/node_modules
test/

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

.next
.development.env
10 changes: 10 additions & 0 deletions create-app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { spawnSync } from "child_process";
import path from "path";
const projectPath = path.resolve(__dirname);
function createApp() {
spawnSync("ts-node ./templates/next/script.ts", { stdio: "inherit", shell: true });
spawnSync("ts-node ./templates/nest/script.ts", { stdio: "inherit", shell: true });
spawnSync(`cp -r ${projectPath}/templates/project/* test`, { stdio: "inherit", shell: true })
spawnSync(`cd test && yarn`, { stdio: "inherit", shell: true })
}
createApp();
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "full-node-cli",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@types/node": "^18.7.19",
"ts-node": "^10.9.1",
"typescript": "^4.8.3"
}
}
98 changes: 98 additions & 0 deletions templates/common/bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable prettier/prettier */
import * as http from 'http';

const mockRequest = {
headers: {
'x-forwarded-for': '127.0.0.1',
host: '127.0.0.1',
hostname: '127.0.0.1',
'Content-Type': '',
'Content-Length': 0,
},
method: 'GET',
url: '/',
socket: {
remoteAddress: '127.0.0.1',
remotePort: 3000,
},
};
function mockHttp(req: any = {}) {
const request = { ...mockRequest, ...req };
const response = new http.ServerResponse(request);
return {
request,
response,
};
}
function mockExpressContext(req?: any) {
const { request, response } = mockHttp(req);
return { req: request, res: response };
}
function mockKoaContext(app: any, req?: any) {
const { request, response } = mockHttp(req);
// 使用 koa 的 createContext 方法创建一个 ctx
const ctx = app.createContext(request, response);
return ctx;
}
export async function getExpressApi(
handleRequest: (req, res) => void,
url: string,
) {
const urlObj = new URL(url);
const mockCtx = mockExpressContext({
url: urlObj.pathname,
path: urlObj.pathname,
method: 'GET',
});
return new Promise((resolve, reject) => {
const res: any = mockCtx.res;
res.send = (body: any) => {
res.body = body;
res.json = () => JSON.parse(body);
res.text = () => body;
resolve(res);
};
handleRequest(mockCtx.req, mockCtx.res);
});
}

export async function getKoaApi(koa: any, url: string) {
const urlObj = new URL(url);
const mockCtx = mockKoaContext(koa, {
url: urlObj.pathname,
path: urlObj.pathname,
method: 'GET',
});
const compose = require('koa-compose');
return new Promise((resolve, reject) => {
const fn = compose([
async (ctx: any, next: () => Promise<any>): Promise<void> => {
try {
await next();
ctx.response.json = () => ctx.response.body;
ctx.response.text = () => JSON.stringify(ctx.response.body);
resolve(ctx.response);
} catch (err) {
reject(err);
}
},
...koa.middleware,
]);
koa.handleRequest(mockCtx, fn);
});
}

const path = require("path");
module.paths.push(path.resolve(__dirname, '../../'));
const requestHandler = require('render');

export function rootMiddleware(req, res, next) {
if (req.url?.startsWith('/api')) {
console.log(`api ${req.url} access`);
return next();
} else {
console.log(`page ${req.url} access`);
return requestHandler(req, res);
}
}
25 changes: 25 additions & 0 deletions templates/midway/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//@ts-nocheck
import { App, Configuration } from '@midwayjs/decorator';
import { ILifeCycle } from '@midwayjs/core';
import { Application } from 'egg';
import { join } from 'path';
import * as egg from '@midwayjs/web';
import { NextMiddleware } from './middleware/next-bridge.middleware';
import { getKoaApi } from './bridge';

@Configuration({
imports: [egg],
importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle implements ILifeCycle {
@App()
app: Application;

async onReady() {
this.app.useMiddleware([NextMiddleware]);
(global as any).serverFetch = getKoaApi.bind(
this,
this.app,
);
}
}
16 changes: 16 additions & 0 deletions templates/midway/next-bridge.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//@ts-nocheck
import { IMiddleware, NextFunction } from '@midwayjs/core';
import { Middleware } from '@midwayjs/decorator';
import { Context } from 'egg';
import { rootMiddleware } from '../bridge';

@Middleware()
export class NextMiddleware implements IMiddleware<Context, NextFunction> {
resolve() {
return async (ctx: Context, next: NextFunction) => rootMiddleware(ctx.req, ctx.res, next);
}

static getName(): string {
return 'next';
}
}
15 changes: 15 additions & 0 deletions templates/nest/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//@ts-nocheck
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { getExpressApi, rootMiddleware } from './bridge';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(rootMiddleware);
(global as any).serverFetch = getExpressApi.bind(
this,
app.getHttpAdapter().getInstance(),
);
await app.listen(process.env.PORT || 3000);
}
bootstrap();
9 changes: 9 additions & 0 deletions templates/nest/script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { spawnSync } from "child_process";
import path from "path";
const projectPath = path.resolve(__dirname, "../../test");
//创建项目
spawnSync(`cd ${projectPath} && git clone https://github.com/nestjs/typescript-starter.git server`, { stdio: "inherit", shell: true });

//将文件覆盖进去
spawnSync(`cd ${projectPath} && cp ../templates/nest/main.ts server/src/`, { stdio: "inherit", shell: true });
spawnSync(`cd ${projectPath} && cp ../templates/common/bridge.ts server/src/`, { stdio: "inherit", shell: true });
16 changes: 16 additions & 0 deletions templates/next/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const next = require('next').default;
const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost';
const port = 3000;
const basePath = process.env.BASE_PATH || '';

const app = next({
dev,
hostname,
port,
dir: '../render',
conf: { basePath, typescript: { ignoreBuildErrors: true } },
});
app.prepare();
const requestHandler = app.getRequestHandler();
module.exports = requestHandler;
45 changes: 45 additions & 0 deletions templates/next/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//@ts-nocheck
import type { NextPage, NextPageContext } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'

const Home: NextPage = ({ data }: { data: string }) => {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="favicon.ico" />
</Head>

<main className={styles.main}>
<h1 className={styles.title}>
data: {data} <br/>(from nest.js controller api)
</h1>
</main>

<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
)
}

export default Home

export async function getServerSideProps(context: NextPageContext) {
const response = await (global as any).serverFetch('http://127.0.0.1/api/hello');
return {
props: { data: response.text() }, // will be passed to the page component as props
}
}
10 changes: 10 additions & 0 deletions templates/next/script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { spawnSync } from "child_process";
import path from "path";
const projectPath = path.resolve(__dirname, "../../test");
//创建项目
spawnSync(`cd ${projectPath} && npx create-next-app@latest --typescript render`, { stdio: "inherit", shell: true });

//将文件覆盖进去
spawnSync(`cd ${projectPath} && mv render/node_modules ./ && mv render/yarn.lock ./`, { stdio: "inherit", shell: true });
spawnSync(`cd ${projectPath} && cp ../templates/next/index.js render/`, { stdio: "inherit", shell: true });
spawnSync(`cd ${projectPath} && cp ../templates/next/page.tsx render/pages/index.tsx`, { stdio: "inherit", shell: true });
40 changes: 40 additions & 0 deletions templates/project/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# compiled output
*/dist
dist
/node_modules
*/node_modules

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

.next
.development.env
33 changes: 33 additions & 0 deletions templates/project/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This stage installs our modules
FROM mhart/alpine-node:14 AS deps
WORKDIR /app
COPY package.json yarn.lock ./
COPY server/package.json ./server/package.json
COPY render/package.json ./render/package.json
RUN yarn --frozen-lockfile

FROM mhart/alpine-node:14 AS serverdeps
WORKDIR /app
COPY package.json yarn.lock ./
COPY server/package.json ./server/package.json
COPY render/package.json ./render/package.json
RUN NODE_ENV=production yarn --frozen-lockfile

FROM deps AS renderbuilder
COPY render ./render
RUN npm run renderBuild

FROM deps AS serverbuilder
COPY server ./server
RUN npm run serverBuild

FROM serverdeps AS runner
COPY --from=renderbuilder /app/render/.next ./render/.next
COPY --from=renderbuilder /app/render/public ./render/public
COPY --from=renderbuilder /app/render/index.js ./render/index.js
COPY --from=serverbuilder /app/server/dist ./server/dist
COPY --from=serverbuilder /app/server/.production.env ./server/

ENV NODE_ENV=production
ENV BASE_PATH=/next-nest
CMD ["npm","run","start"]
Loading

0 comments on commit b2e82af

Please sign in to comment.