From 9683ebca24cb0e80068c2faec786455e5dd1d06e Mon Sep 17 00:00:00 2001 From: Nicholas Cunningham Date: Fri, 9 Feb 2024 14:23:12 -0700 Subject: [PATCH] fix(nextjs): Custom server should work with Crystal (#21736) --- e2e/next-core/src/next.test.ts | 34 ++++++++++++++ .../custom-server/custom-server.spec.ts | 9 ---- .../generators/custom-server/custom-server.ts | 47 +++++++++++++++---- .../files/server/main.ts__tmpl__ | 8 ++-- .../files/tsconfig.server.json__tmpl__ | 2 + .../src/utils/add-swc-to-custom-server.ts | 12 ++++- 6 files changed, 88 insertions(+), 24 deletions(-) diff --git a/e2e/next-core/src/next.test.ts b/e2e/next-core/src/next.test.ts index 4bf16c3132..eb933a6808 100644 --- a/e2e/next-core/src/next.test.ts +++ b/e2e/next-core/src/next.test.ts @@ -149,6 +149,40 @@ describe('Next.js Applications', () => { checkExport: false, }); }, 300_000); + + it('should support --custom-server flag (swc)', async () => { + const appName = uniq('app'); + + runCLI(`generate @nx/next:app ${appName} --no-interactive --custom-server`); + + checkFilesExist(`apps/${appName}/server/main.ts`); + + const result = runCLI(`build ${appName}`); + + checkFilesExist(`dist/apps/${appName}/server/main.js`); + + expect(result).toContain( + `Successfully ran target build for project ${appName}` + ); + }, 300_000); + + it('should support --custom-server flag (tsc)', async () => { + const appName = uniq('app'); + + runCLI( + `generate @nx/next:app ${appName} --swc=false --no-interactive --custom-server` + ); + + checkFilesExist(`apps/${appName}/server/main.ts`); + + const result = runCLI(`build ${appName}`); + + checkFilesExist(`dist/apps/${appName}/server/main.js`); + + expect(result).toContain( + `Successfully ran target build for project ${appName}` + ); + }, 300_000); }); function getData(port, path = ''): Promise { diff --git a/packages/next/src/generators/custom-server/custom-server.spec.ts b/packages/next/src/generators/custom-server/custom-server.spec.ts index 7fe1573236..b7e6f2062a 100644 --- a/packages/next/src/generators/custom-server/custom-server.spec.ts +++ b/packages/next/src/generators/custom-server/custom-server.spec.ts @@ -5,16 +5,7 @@ import { applicationGenerator } from '../application/application'; describe('app', () => { let tree: Tree; - let originalEnv: string; beforeAll(() => { - originalEnv = process.env.NX_ADD_PLUGINS; - process.env.NX_ADD_PLUGINS = 'false'; - }); - afterAll(() => { - process.env.NX_ADD_PLUGINS = originalEnv; - }); - - beforeEach(() => { tree = createTreeWithEmptyWorkspace(); }); diff --git a/packages/next/src/generators/custom-server/custom-server.ts b/packages/next/src/generators/custom-server/custom-server.ts index e109d18705..48b34f8e46 100644 --- a/packages/next/src/generators/custom-server/custom-server.ts +++ b/packages/next/src/generators/custom-server/custom-server.ts @@ -6,6 +6,7 @@ import { offsetFromRoot, readProjectConfiguration, updateProjectConfiguration, + readNxJson, } from '@nx/devkit'; import { CustomServerSchema } from './schema'; import { join } from 'path'; @@ -17,9 +18,17 @@ export async function customServerGenerator( ) { const project = readProjectConfiguration(host, options.project); + const nxJson = readNxJson(host); + const hasPlugin = nxJson.plugins?.some((p) => + typeof p === 'string' + ? p === '@nx/next/plugin' + : p.plugin === '@nx/next/plugin' + ); + if ( project.targets?.build?.executor !== '@nx/next:build' && - project.targets?.build?.executor !== '@nrwl/next:build' + project.targets?.build?.executor !== '@nrwl/next:build' && + !hasPlugin ) { logger.error( `Project ${options.project} is not a Next.js project. Did you generate it with "nx g @nx/next:app"?` @@ -27,14 +36,18 @@ export async function customServerGenerator( return; } - const outputPath = project.targets?.build?.options?.outputPath; + // In Nx 18 next artifacts are inside the project root .next/ & dist/ (for custom server) + const outputPath = hasPlugin + ? `dist/${project.root}` + : project.targets?.build?.options?.outputPath; const root = project.root; if ( - !root || - !outputPath || - !project.targets?.build?.configurations?.development || - !project.targets?.build?.configurations?.production + (!root || + !outputPath || + !project.targets?.build?.configurations?.development || + !project.targets?.build?.configurations?.production) && + !hasPlugin ) { logger.error( `Project ${options.project} has invalid config. Did you generate it with "nx g @nx/next:app"?` @@ -52,17 +65,31 @@ export async function customServerGenerator( return; } + // In Nx 18 next artifacts are inside the project root .next/ & dist/ (for custom server) + // So we need ensure the mapping is correct from dist to the project root + const projectPathFromDist = `../../${offsetFromRoot(project.root)}${ + project.root + }`; + generateFiles(host, join(__dirname, 'files'), project.root, { ...options, + hasPlugin, + projectPathFromDist, offsetFromRoot: offsetFromRoot(project.root), projectRoot: project.root, tmpl: '', }); - project.targets.build.dependsOn = ['build-custom-server']; - project.targets.serve.options.customServerTarget = `${options.project}:serve-custom-server`; - project.targets.serve.configurations.development.customServerTarget = `${options.project}:serve-custom-server:development`; - project.targets.serve.configurations.production.customServerTarget = `${options.project}:serve-custom-server:production`; + if (!hasPlugin) { + project.targets.build.dependsOn = ['build-custom-server']; + project.targets.serve.options.customServerTarget = `${options.project}:serve-custom-server`; + project.targets.serve.configurations.development.customServerTarget = `${options.project}:serve-custom-server:development`; + project.targets.serve.configurations.production.customServerTarget = `${options.project}:serve-custom-server:production`; + } else { + project.targets['build'] = { + dependsOn: ['^build', 'build-custom-server'], + }; + } project.targets['build-custom-server'] = { executor: options.compiler === 'tsc' ? '@nx/js:tsc' : '@nx/js:swc', diff --git a/packages/next/src/generators/custom-server/files/server/main.ts__tmpl__ b/packages/next/src/generators/custom-server/files/server/main.ts__tmpl__ index cb97705f92..0ec46fe560 100644 --- a/packages/next/src/generators/custom-server/files/server/main.ts__tmpl__ +++ b/packages/next/src/generators/custom-server/files/server/main.ts__tmpl__ @@ -2,9 +2,8 @@ * This is only a minimal custom server to get started. * You may want to consider using Express or another server framework, and enable security features such as CORS. * - * For more examples, see the Next.js repo: - * - Express: https://github.com/vercel/next.js/tree/canary/examples/custom-server-express - * - Hapi: https://github.com/vercel/next.js/tree/canary/examples/custom-server-hapi + * For an example, see the Next.js repo: + * Node - https://github.com/vercel/next.js/blob/canary/examples/custom-server */ import { createServer } from 'http'; import { parse } from 'node:url'; @@ -15,7 +14,8 @@ import next from 'next'; // - The environment variable is set by `@nx/next:server` when running the dev server. // - The fallback `__dirname` is for production builds. // - Feel free to change this to suit your needs. -const dir = process.env.NX_NEXT_DIR || path.join(__dirname, '..'); + +const dir = process.env.NX_NEXT_DIR || <%- hasPlugin ? `path.join(__dirname, '${projectPathFromDist}')` : `path.join(__dirname, '..')`; %> const dev = process.env.NODE_ENV === 'development'; // HTTP Server options: diff --git a/packages/next/src/generators/custom-server/files/tsconfig.server.json__tmpl__ b/packages/next/src/generators/custom-server/files/tsconfig.server.json__tmpl__ index 4214a917d7..add47ecc00 100644 --- a/packages/next/src/generators/custom-server/files/tsconfig.server.json__tmpl__ +++ b/packages/next/src/generators/custom-server/files/tsconfig.server.json__tmpl__ @@ -4,7 +4,9 @@ "module": "commonjs", "noEmit": false, "incremental": true, + <% if(hasPlugin && compiler === 'tsc') { %> "tsBuildInfoFile": "<%= offsetFromRoot %>tmp/buildcache/<%= projectRoot %>/server", + <% } %> "types": [ "node" ] diff --git a/packages/next/src/utils/add-swc-to-custom-server.ts b/packages/next/src/utils/add-swc-to-custom-server.ts index 563044bc90..fc49e942d9 100644 --- a/packages/next/src/utils/add-swc-to-custom-server.ts +++ b/packages/next/src/utils/add-swc-to-custom-server.ts @@ -4,6 +4,7 @@ import { installPackagesTask, joinPathFragments, readJson, + updateJson, } from '@nx/devkit'; import { swcCliVersion, swcCoreVersion, swcNodeVersion } from './versions'; import { addSwcConfig } from '@nx/js/src/utils/swc/add-swc-config'; @@ -21,7 +22,16 @@ export function configureForSwc(tree: Tree, projectRoot: string) { rootPackageJson.devDependencies?.['@swc/cli']; if (!tree.exists(swcConfigPath)) { - addSwcConfig(tree, swcConfigPath); + addSwcConfig(tree, projectRoot); + } + + if (tree.exists(swcConfigPath)) { + updateJson(tree, swcConfigPath, (json) => { + return { + ...json, + exclude: [...json.exclude, '.*.d.ts$'], + }; + }); } if (!hasSwcDepedency || !hasSwcCliDependency) {