diff --git a/e2e/next/src/next-legacy.test.ts b/e2e/next/src/next-legacy.test.ts index 01b15a1e287ca..c1418f45ebe33 100644 --- a/e2e/next/src/next-legacy.test.ts +++ b/e2e/next/src/next-legacy.test.ts @@ -279,4 +279,44 @@ describe('@nx/next (legacy)', () => { await killPort(prodServePort); await killPort(selfContainedPort); }, 600_000); + + it('should support --custom-server flag (swc)', async () => { + const appName = uniq('app'); + + runCLI( + `generate @nx/next:app ${appName} --no-interactive --custom-server --linter=eslint --unitTestRunner=jest`, + { env: { NX_ADD_PLUGINS: 'false' } } + ); + + // Check for custom server files added to source + checkFilesExist(`${appName}/server/main.ts`); + checkFilesExist(`${appName}/.server.swcrc`); + + const result = runCLI(`build ${appName}`); + + checkFilesExist(`dist/${appName}-server/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 --linter=eslint --unitTestRunner=jest`, + { env: { NX_ADD_PLUGINS: 'false' } } + ); + + checkFilesExist(`${appName}/server/main.ts`); + + const result = runCLI(`build ${appName}`); + + checkFilesExist(`dist/${appName}-server/server/main.js`); + + expect(result).toContain( + `Successfully ran target build for project ${appName}` + ); + }, 300_000); }); diff --git a/e2e/next/src/next.test.ts b/e2e/next/src/next.test.ts index fcf104e28b9f1..0c3e579ba5b51 100644 --- a/e2e/next/src/next.test.ts +++ b/e2e/next/src/next.test.ts @@ -158,11 +158,13 @@ describe('Next.js Applications', () => { `generate @nx/next:app ${appName} --no-interactive --custom-server --linter=eslint --unitTestRunner=jest` ); + // Check for custom server files added to source checkFilesExist(`${appName}/server/main.ts`); + checkFilesExist(`${appName}/.server.swcrc`); const result = runCLI(`build ${appName}`); - checkFilesExist(`dist/${appName}/server/main.js`); + checkFilesExist(`dist/${appName}-server/server/main.js`); expect(result).toContain( `Successfully ran target build for project ${appName}` @@ -180,7 +182,7 @@ describe('Next.js Applications', () => { const result = runCLI(`build ${appName}`); - checkFilesExist(`dist/${appName}/server/main.js`); + checkFilesExist(`dist/${appName}-server/server/main.js`); expect(result).toContain( `Successfully ran target build for project ${appName}` diff --git a/packages/js/src/executors/node/node.impl.ts b/packages/js/src/executors/node/node.impl.ts index a50474a613bf1..d31cbeb0ec265 100644 --- a/packages/js/src/executors/node/node.impl.ts +++ b/packages/js/src/executors/node/node.impl.ts @@ -145,7 +145,7 @@ export async function* nodeExecutor( // Wait for build to finish. const result = await buildResult; - if (!result.success) { + if (result && !result.success) { // If in watch-mode, don't throw or else the process exits. if (options.watch) { if (!task.killed) { diff --git a/packages/js/src/utils/swc/add-swc-config.ts b/packages/js/src/utils/swc/add-swc-config.ts index f6783e7bffcf8..547af1138c0da 100644 --- a/packages/js/src/utils/swc/add-swc-config.ts +++ b/packages/js/src/utils/swc/add-swc-config.ts @@ -54,11 +54,20 @@ export function addSwcConfig( tree: Tree, projectDir: string, type: 'commonjs' | 'es6' = 'commonjs', - supportTsx: boolean = false + supportTsx: boolean = false, + swcName: string = '.swcrc', + additionalExcludes: string[] = [] ) { - const swcrcPath = join(projectDir, '.swcrc'); + const swcrcPath = join(projectDir, swcName); if (tree.exists(swcrcPath)) return; - tree.write(swcrcPath, swcOptionsString(type, defaultExclude, supportTsx)); + tree.write( + swcrcPath, + swcOptionsString( + type, + [...defaultExclude, ...additionalExcludes], + supportTsx + ) + ); } export function addSwcTestConfig( diff --git a/packages/next/src/generators/application/application.ts b/packages/next/src/generators/application/application.ts index a99c7f889eed4..703868f5d8004 100644 --- a/packages/next/src/generators/application/application.ts +++ b/packages/next/src/generators/application/application.ts @@ -32,6 +32,7 @@ import { updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; +import { configureForSwc } from '../../utils/add-swc-to-custom-server'; export async function applicationGenerator(host: Tree, schema: Schema) { return await applicationGeneratorInternal(host, { @@ -93,6 +94,11 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) { updateCypressTsConfig(host, options); setDefaults(host, options); + if (options.swc) { + const swcTask = configureForSwc(host, options.appProjectRoot); + tasks.push(swcTask); + } + if (options.customServer) { await customServerGenerator(host, { project: options.projectName, diff --git a/packages/next/src/generators/custom-server/custom-server.ts b/packages/next/src/generators/custom-server/custom-server.ts index 4c2e6af45d42c..235c2ff6ea1b0 100644 --- a/packages/next/src/generators/custom-server/custom-server.ts +++ b/packages/next/src/generators/custom-server/custom-server.ts @@ -18,6 +18,7 @@ export async function customServerGenerator( options: CustomServerSchema ) { const project = readProjectConfiguration(host, options.project); + const swcServerName = '.server.swcrc'; const nxJson = readNxJson(host); const hasPlugin = nxJson.plugins?.some((p) => @@ -26,11 +27,7 @@ export async function customServerGenerator( : p.plugin === '@nx/next/plugin' ); - if ( - project.targets?.build?.executor !== '@nx/next:build' && - project.targets?.build?.executor !== '@nrwl/next:build' && - !hasPlugin - ) { + if (project.targets?.build?.executor !== '@nx/next:build' && !hasPlugin) { logger.error( `Project ${options.project} is not a Next.js project. Did you generate it with "nx g @nx/next:app"?` ); @@ -38,9 +35,7 @@ export async function customServerGenerator( } // 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 outputPath = `dist/${project.root}-server`; const root = project.root; if ( @@ -68,9 +63,9 @@ export async function customServerGenerator( // 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 - }`; + const projectPathFromDist = hasPlugin + ? `../../${offsetFromRoot(project.root)}${project.root}` + : `${offsetFromRoot(`dist/${project.root}`)}${project.root}`; const offset = offsetFromRoot(project.root); const isTsSolution = isUsingTsSolutionSetup(host); @@ -107,6 +102,9 @@ export async function customServerGenerator( tsConfig: `${root}/tsconfig.server.json`, clean: false, assets: [], + ...(options.compiler === 'tsc' + ? {} + : { swcrc: `${root}/${swcServerName}` }), }, configurations: { development: {}, @@ -150,6 +148,11 @@ export async function customServerGenerator( }); if (options.compiler === 'swc') { - return configureForSwc(host, project.root); + // Update app swc to exlude server files + updateJson(host, join(project.root, '.swcrc'), (json) => { + json.exclude = [...(json.exclude ?? []), 'server/**']; + return json; + }); + return configureForSwc(host, project.root, swcServerName, ['src/**/*']); } } 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 0ec46fe5607a4..3a22b42538aae 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__ @@ -15,8 +15,8 @@ import next from 'next'; // - The fallback `__dirname` is for production builds. // - Feel free to change this to suit your needs. -const dir = process.env.NX_NEXT_DIR || <%- hasPlugin ? `path.join(__dirname, '${projectPathFromDist}')` : `path.join(__dirname, '..')`; %> const dev = process.env.NODE_ENV === 'development'; +const dir = process.env.NX_NEXT_DIR || <%- hasPlugin ? `path.join(__dirname, '${projectPathFromDist}')` : `path.join(__dirname, dev ? '..' : '', '${projectPathFromDist}')`; %> // HTTP Server options: // - Feel free to change this to suit your needs. 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 e7bade6a1e61a..8c08d4ca9edf2 100644 --- a/packages/next/src/utils/add-swc-to-custom-server.ts +++ b/packages/next/src/utils/add-swc-to-custom-server.ts @@ -4,7 +4,6 @@ import { installPackagesTask, joinPathFragments, readJson, - updateJson, } from '@nx/devkit'; import { swcCliVersion, @@ -14,8 +13,13 @@ import { } from '@nx/js/src/utils/versions'; import { addSwcConfig } from '@nx/js/src/utils/swc/add-swc-config'; -export function configureForSwc(tree: Tree, projectRoot: string) { - const swcConfigPath = joinPathFragments(projectRoot, '.swcrc'); +export function configureForSwc( + tree: Tree, + projectRoot: string, + swcConfigName = '.swcrc', + additonalExludes: string[] = [] +) { + const swcConfigPath = joinPathFragments(projectRoot, swcConfigName); const rootPackageJson = readJson(tree, 'package.json'); const hasSwcDepedency = @@ -27,22 +31,17 @@ export function configureForSwc(tree: Tree, projectRoot: string) { rootPackageJson.devDependencies?.['@swc/cli']; if (!tree.exists(swcConfigPath)) { - addSwcConfig(tree, projectRoot); - } - - if (tree.exists(swcConfigPath)) { - updateJson(tree, swcConfigPath, (json) => { - return { - ...json, - exclude: [...json.exclude, '.*.d.ts$'], - }; - }); + // We need to create a swc config file specific for custom server + addSwcConfig(tree, projectRoot, 'commonjs', false, swcConfigName, [ + ...additonalExludes, + '.*.d.ts$', + ]); } if (!hasSwcDepedency || !hasSwcCliDependency) { addSwcDependencies(tree); - return () => installPackagesTask(tree); } + return () => installPackagesTask(tree); } function addSwcDependencies(tree: Tree) {