From 451ecf5f851a115aea0fb533dacdf3b4c06980dd Mon Sep 17 00:00:00 2001 From: kongjiacong Date: Wed, 11 Dec 2024 15:04:11 +0800 Subject: [PATCH 1/4] fix: should get server routes from route.json in serve command --- .../src/adapters/node/plugins/resource.ts | 3 +- .../server/core/src/plugins/render/render.ts | 5 +- packages/server/core/src/utils/entry.ts | 5 ++ .../src/plugins/analyze/getServerRoutes.ts | 26 +++++++--- .../app-tools/src/plugins/analyze/index.ts | 38 +++++++++++---- .../app-tools/src/plugins/analyze/utils.ts | 6 +++ .../ssg/fixtures/nested-routes/package.json | 3 +- .../ssg/tests/nested-routes.test.ts | 48 +++++++++++++++++-- 8 files changed, 111 insertions(+), 23 deletions(-) diff --git a/packages/server/core/src/adapters/node/plugins/resource.ts b/packages/server/core/src/adapters/node/plugins/resource.ts index bde2467393b2..a311df9b90be 100644 --- a/packages/server/core/src/adapters/node/plugins/resource.ts +++ b/packages/server/core/src/adapters/node/plugins/resource.ts @@ -16,6 +16,7 @@ import type { ServerManifest, ServerPlugin, } from '../../../types'; +import { uniqueKeyByRoute } from '../../../utils'; export async function getHtmlTemplates(pwd: string, routes: ServerRoute[]) { const htmls = await Promise.all( @@ -27,7 +28,7 @@ export async function getHtmlTemplates(pwd: string, routes: ServerRoute[]) { } catch (e) { // ignore error } - return [route.entryName!, html]; + return [uniqueKeyByRoute(route), html]; }) || [], ); diff --git a/packages/server/core/src/plugins/render/render.ts b/packages/server/core/src/plugins/render/render.ts index 13770f13d0bc..4f8a56a3e8c8 100644 --- a/packages/server/core/src/plugins/render/render.ts +++ b/packages/server/core/src/plugins/render/render.ts @@ -12,6 +12,7 @@ import type { } from '../../types'; import type { Render } from '../../types'; import type { Params } from '../../types/requestHandler'; +import { uniqueKeyByRoute } from '../../utils'; import { ErrorDigest, createErrorHtml, @@ -120,6 +121,7 @@ export async function createRender({ }, ) => { const forMatchpathname = matchPathname ?? getPathname(req); + const [routeInfo, params] = matchRoute(router, forMatchpathname); const framework = metaName || 'modern-js'; const fallbackHeader = `x-${cutNameByHyphen(framework)}-ssr-fallback`; @@ -139,8 +141,7 @@ export async function createRender({ }); } - const html = templates[routeInfo.entryName!]; - + const html = templates[uniqueKeyByRoute(routeInfo)]; if (!html) { return new Response(createErrorHtml(404), { status: 404, diff --git a/packages/server/core/src/utils/entry.ts b/packages/server/core/src/utils/entry.ts index 32b92558a674..b29aca850d14 100644 --- a/packages/server/core/src/utils/entry.ts +++ b/packages/server/core/src/utils/entry.ts @@ -3,3 +3,8 @@ import type { ServerRoute } from '@modern-js/types'; export const sortRoutes = (route1: ServerRoute, route2: ServerRoute) => { return route2.urlPath.length - route1.urlPath.length; }; + +// Because the same entryName may have different urlPath(ssg), so we need to add urlPath as key +export const uniqueKeyByRoute = (route: ServerRoute) => { + return `${route.entryName!}-${route.urlPath}`; +}; diff --git a/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts b/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts index 1f250db56ae1..2d07708b87be 100644 --- a/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts +++ b/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts @@ -1,8 +1,8 @@ -import fs from 'fs'; import path from 'path'; -import type { IAppContext } from '@modern-js/core'; import type { Entrypoint, ServerRoute } from '@modern-js/types'; import { + fs, + ROUTE_SPEC_FILE, SERVER_BUNDLE_DIRECTORY, SERVER_WORKER_BUNDLE_DIRECTORY, getEntryOptions, @@ -245,10 +245,24 @@ export const getServerRoutes = ( appContext: AppToolsContext<'shared'>; config: AppNormalizedConfig<'shared'>; }, -): ServerRoute[] => [ - ...collectHtmlRoutes(entrypoints, appContext, config), - ...collectStaticRoutes(appContext, config), -]; +): ServerRoute[] => { + return [ + ...collectHtmlRoutes(entrypoints, appContext, config), + ...collectStaticRoutes(appContext, config), + ]; +}; const toPosix = (pathStr: string) => pathStr.split(path.sep).join(path.posix.sep); + +export const getServerRoutesServe = (distDirectory: string) => { + const routeJSON = path.join(distDirectory, ROUTE_SPEC_FILE); + try { + const { routes } = fs.readJSONSync(routeJSON); + return routes; + } catch (e) { + throw new Error( + `Failed to read routes from ${routeJSON}, please check if the file exists.`, + ); + } +}; diff --git a/packages/solutions/app-tools/src/plugins/analyze/index.ts b/packages/solutions/app-tools/src/plugins/analyze/index.ts index a3ad4b81da26..1cb5ab20f7ef 100644 --- a/packages/solutions/app-tools/src/plugins/analyze/index.ts +++ b/packages/solutions/app-tools/src/plugins/analyze/index.ts @@ -1,4 +1,5 @@ import * as path from 'path'; +import type { ServerRoute } from '@modern-js/types'; import { fs, createDebugger, @@ -18,7 +19,7 @@ import { emitResolvedConfig } from '../../utils/config'; import { getSelectedEntries } from '../../utils/getSelectedEntries'; import { printInstructions } from '../../utils/printInstructions'; import { generateRoutes } from '../../utils/routes'; -import { checkIsBuildCommands } from './utils'; +import { checkIsBuildCommands, checkIsServeCommand } from './utils'; const debug = createDebugger('plugin-analyze'); @@ -54,8 +55,21 @@ export default ({ ); await hooks.addRuntimeExports.call(); + const [{ getServerRoutesServe }] = await Promise.all([ + import('./getServerRoutes.js'), + ]); + if (apiOnly) { - const { routes } = await hooks.modifyServerRoutes.call({ routes: [] }); + const routes: ServerRoute[] = []; + if (checkIsServeCommand()) { + routes.push(...getServerRoutesServe(appContext.distDirectory)); + } else { + const { routes: modifiedRoutes } = + await hooks.modifyServerRoutes.call({ + routes: [], + }); + routes.push(...modifiedRoutes); + } debug(`server routes: %o`, routes); @@ -80,14 +94,20 @@ export default ({ debug(`entrypoints: %o`, entrypoints); - const initialRoutes = getServerRoutes(entrypoints, { - appContext, - config: resolvedConfig, - }); + const routes: ServerRoute[] = []; + if (checkIsServeCommand()) { + routes.push(...getServerRoutesServe(appContext.distDirectory)); + } else { + const initialRoutes = getServerRoutes(entrypoints, { + appContext, + config: resolvedConfig, + }); - const { routes } = await hooks.modifyServerRoutes.call({ - routes: initialRoutes, - }); + const { routes: modifiedRoutes } = await hooks.modifyServerRoutes.call({ + routes: initialRoutes, + }); + routes.push(...modifiedRoutes); + } debug(`server routes: %o`, routes); diff --git a/packages/solutions/app-tools/src/plugins/analyze/utils.ts b/packages/solutions/app-tools/src/plugins/analyze/utils.ts index 100a977dc67a..9716221ff9d1 100644 --- a/packages/solutions/app-tools/src/plugins/analyze/utils.ts +++ b/packages/solutions/app-tools/src/plugins/analyze/utils.ts @@ -74,6 +74,12 @@ export const checkIsBuildCommands = () => { return buildCommands.includes(command); }; +export const checkIsServeCommand = () => { + const command = getCommand(); + + return command === 'serve'; +}; + export const isSubDirOrEqual = (parent: string, child: string): boolean => { if (parent === child) { return true; diff --git a/tests/integration/ssg/fixtures/nested-routes/package.json b/tests/integration/ssg/fixtures/nested-routes/package.json index f2379a59a368..2482bbb36ee1 100644 --- a/tests/integration/ssg/fixtures/nested-routes/package.json +++ b/tests/integration/ssg/fixtures/nested-routes/package.json @@ -3,7 +3,8 @@ "name": "ssg-fixtures-nested-routes", "version": "2.9.0", "scripts": { - "build": "modern build" + "build": "modern build", + "serve": "modern serve" }, "dependencies": { "@modern-js/app-tools": "workspace:*", diff --git a/tests/integration/ssg/tests/nested-routes.test.ts b/tests/integration/ssg/tests/nested-routes.test.ts index db121d0c095e..efe3613e5eca 100644 --- a/tests/integration/ssg/tests/nested-routes.test.ts +++ b/tests/integration/ssg/tests/nested-routes.test.ts @@ -1,17 +1,22 @@ import path, { join } from 'path'; import { fs } from '@modern-js/utils'; -import { killApp, modernBuild } from '../../../utils/modernTestUtils'; +import puppeteer from 'puppeteer'; +import { + getPort, + killApp, + launchOptions, + modernBuild, + modernServe, +} from '../../../utils/modernTestUtils'; const fixtureDir = path.resolve(__dirname, '../fixtures'); - +const appDir = join(fixtureDir, 'nested-routes'); jest.setTimeout(1000 * 60 * 3); describe('ssg', () => { let app: any; - let appDir: string; let distDir: string; beforeAll(async () => { - appDir = join(fixtureDir, 'nested-routes'); distDir = join(appDir, './dist'); await modernBuild(appDir); }); @@ -31,3 +36,38 @@ describe('ssg', () => { expect(html.includes('Hello, User')).toBe(true); }); }); + +describe('test ssg request', () => { + let buildRes: { code: number }; + let app: any; + let port: any; + beforeAll(async () => { + port = await getPort(); + + buildRes = await modernBuild(appDir); + app = await modernServe(appDir, port, { + cwd: appDir, + }); + }); + + afterAll(async () => { + await killApp(app); + }); + + test('should support enableInlineScripts', async () => { + const host = `http://localhost`; + expect(buildRes.code === 0).toBe(true); + const browser = await puppeteer.launch(launchOptions as any); + const page = await browser.newPage(); + await page.goto(`${host}:${port}/user`); + + const description = await page.$('#data'); + const targetText = await page.evaluate(el => el?.textContent, description); + try { + expect(targetText?.trim()).toEqual('Hello, User'); + } finally { + await page.close(); + await browser.close(); + } + }); +}); From 7a81a922f0aa568c26d9b98706b0ecf2a8a73a67 Mon Sep 17 00:00:00 2001 From: kongjiacong Date: Wed, 11 Dec 2024 15:37:16 +0800 Subject: [PATCH 2/4] chore: modify function name --- .../app-tools/src/plugins/analyze/getServerRoutes.ts | 2 +- packages/solutions/app-tools/src/plugins/analyze/index.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts b/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts index 2d07708b87be..eb8976b04632 100644 --- a/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts +++ b/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts @@ -255,7 +255,7 @@ export const getServerRoutes = ( const toPosix = (pathStr: string) => pathStr.split(path.sep).join(path.posix.sep); -export const getServerRoutesServe = (distDirectory: string) => { +export const getProdServerRoutes = (distDirectory: string) => { const routeJSON = path.join(distDirectory, ROUTE_SPEC_FILE); try { const { routes } = fs.readJSONSync(routeJSON); diff --git a/packages/solutions/app-tools/src/plugins/analyze/index.ts b/packages/solutions/app-tools/src/plugins/analyze/index.ts index 1cb5ab20f7ef..3d0ccf01f1c6 100644 --- a/packages/solutions/app-tools/src/plugins/analyze/index.ts +++ b/packages/solutions/app-tools/src/plugins/analyze/index.ts @@ -55,14 +55,14 @@ export default ({ ); await hooks.addRuntimeExports.call(); - const [{ getServerRoutesServe }] = await Promise.all([ + const [{ getProdServerRoutes }] = await Promise.all([ import('./getServerRoutes.js'), ]); if (apiOnly) { const routes: ServerRoute[] = []; if (checkIsServeCommand()) { - routes.push(...getServerRoutesServe(appContext.distDirectory)); + routes.push(...getProdServerRoutes(appContext.distDirectory)); } else { const { routes: modifiedRoutes } = await hooks.modifyServerRoutes.call({ @@ -96,7 +96,7 @@ export default ({ const routes: ServerRoute[] = []; if (checkIsServeCommand()) { - routes.push(...getServerRoutesServe(appContext.distDirectory)); + routes.push(...getProdServerRoutes(appContext.distDirectory)); } else { const initialRoutes = getServerRoutes(entrypoints, { appContext, From 161183016fc02c4b0e78926ae199b82d61e6e4cc Mon Sep 17 00:00:00 2001 From: kongjiacong Date: Wed, 11 Dec 2024 15:40:04 +0800 Subject: [PATCH 3/4] chore: cr --- packages/server/core/src/plugins/render/render.ts | 1 - .../app-tools/src/plugins/analyze/getServerRoutes.ts | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/server/core/src/plugins/render/render.ts b/packages/server/core/src/plugins/render/render.ts index 4f8a56a3e8c8..7b538479151f 100644 --- a/packages/server/core/src/plugins/render/render.ts +++ b/packages/server/core/src/plugins/render/render.ts @@ -121,7 +121,6 @@ export async function createRender({ }, ) => { const forMatchpathname = matchPathname ?? getPathname(req); - const [routeInfo, params] = matchRoute(router, forMatchpathname); const framework = metaName || 'modern-js'; const fallbackHeader = `x-${cutNameByHyphen(framework)}-ssr-fallback`; diff --git a/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts b/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts index eb8976b04632..1c21b233d7a7 100644 --- a/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts +++ b/packages/solutions/app-tools/src/plugins/analyze/getServerRoutes.ts @@ -245,12 +245,10 @@ export const getServerRoutes = ( appContext: AppToolsContext<'shared'>; config: AppNormalizedConfig<'shared'>; }, -): ServerRoute[] => { - return [ - ...collectHtmlRoutes(entrypoints, appContext, config), - ...collectStaticRoutes(appContext, config), - ]; -}; +): ServerRoute[] => [ + ...collectHtmlRoutes(entrypoints, appContext, config), + ...collectStaticRoutes(appContext, config), +]; const toPosix = (pathStr: string) => pathStr.split(path.sep).join(path.posix.sep); From a7f5028880482a03323c5874d221e89a8b956988 Mon Sep 17 00:00:00 2001 From: kongjiacong Date: Wed, 11 Dec 2024 16:00:51 +0800 Subject: [PATCH 4/4] docs: add changeset --- .changeset/soft-spoons-greet.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/soft-spoons-greet.md diff --git a/.changeset/soft-spoons-greet.md b/.changeset/soft-spoons-greet.md new file mode 100644 index 000000000000..8986cf22c62b --- /dev/null +++ b/.changeset/soft-spoons-greet.md @@ -0,0 +1,7 @@ +--- +'@modern-js/app-tools': patch +'@modern-js/server-core': patch +--- + +fix: should get server routes from route.json in serve command +fix: 在 serve 命令下应该从 route.json 中获取 server routes