fix(nextjs): Custom server should work with Crystal (#21736)

This commit is contained in:
Nicholas Cunningham 2024-02-09 14:23:12 -07:00 committed by GitHub
parent 70ea82d58c
commit 9683ebca24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 88 additions and 24 deletions

View File

@ -149,6 +149,40 @@ describe('Next.js Applications', () => {
checkExport: false, checkExport: false,
}); });
}, 300_000); }, 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<any> { function getData(port, path = ''): Promise<any> {

View File

@ -5,16 +5,7 @@ import { applicationGenerator } from '../application/application';
describe('app', () => { describe('app', () => {
let tree: Tree; let tree: Tree;
let originalEnv: string;
beforeAll(() => { beforeAll(() => {
originalEnv = process.env.NX_ADD_PLUGINS;
process.env.NX_ADD_PLUGINS = 'false';
});
afterAll(() => {
process.env.NX_ADD_PLUGINS = originalEnv;
});
beforeEach(() => {
tree = createTreeWithEmptyWorkspace(); tree = createTreeWithEmptyWorkspace();
}); });

View File

@ -6,6 +6,7 @@ import {
offsetFromRoot, offsetFromRoot,
readProjectConfiguration, readProjectConfiguration,
updateProjectConfiguration, updateProjectConfiguration,
readNxJson,
} from '@nx/devkit'; } from '@nx/devkit';
import { CustomServerSchema } from './schema'; import { CustomServerSchema } from './schema';
import { join } from 'path'; import { join } from 'path';
@ -17,9 +18,17 @@ export async function customServerGenerator(
) { ) {
const project = readProjectConfiguration(host, options.project); 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 ( if (
project.targets?.build?.executor !== '@nx/next:build' && project.targets?.build?.executor !== '@nx/next:build' &&
project.targets?.build?.executor !== '@nrwl/next:build' project.targets?.build?.executor !== '@nrwl/next:build' &&
!hasPlugin
) { ) {
logger.error( logger.error(
`Project ${options.project} is not a Next.js project. Did you generate it with "nx g @nx/next:app"?` `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; 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; const root = project.root;
if ( if (
!root || (!root ||
!outputPath || !outputPath ||
!project.targets?.build?.configurations?.development || !project.targets?.build?.configurations?.development ||
!project.targets?.build?.configurations?.production !project.targets?.build?.configurations?.production) &&
!hasPlugin
) { ) {
logger.error( logger.error(
`Project ${options.project} has invalid config. Did you generate it with "nx g @nx/next:app"?` `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; 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, { generateFiles(host, join(__dirname, 'files'), project.root, {
...options, ...options,
hasPlugin,
projectPathFromDist,
offsetFromRoot: offsetFromRoot(project.root), offsetFromRoot: offsetFromRoot(project.root),
projectRoot: project.root, projectRoot: project.root,
tmpl: '', tmpl: '',
}); });
project.targets.build.dependsOn = ['build-custom-server']; if (!hasPlugin) {
project.targets.serve.options.customServerTarget = `${options.project}:serve-custom-server`; project.targets.build.dependsOn = ['build-custom-server'];
project.targets.serve.configurations.development.customServerTarget = `${options.project}:serve-custom-server:development`; project.targets.serve.options.customServerTarget = `${options.project}:serve-custom-server`;
project.targets.serve.configurations.production.customServerTarget = `${options.project}:serve-custom-server:production`; 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'] = { project.targets['build-custom-server'] = {
executor: options.compiler === 'tsc' ? '@nx/js:tsc' : '@nx/js:swc', executor: options.compiler === 'tsc' ? '@nx/js:tsc' : '@nx/js:swc',

View File

@ -2,9 +2,8 @@
* This is only a minimal custom server to get started. * 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. * 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: * For an example, see the Next.js repo:
* - Express: https://github.com/vercel/next.js/tree/canary/examples/custom-server-express * Node - https://github.com/vercel/next.js/blob/canary/examples/custom-server
* - Hapi: https://github.com/vercel/next.js/tree/canary/examples/custom-server-hapi
*/ */
import { createServer } from 'http'; import { createServer } from 'http';
import { parse } from 'node:url'; 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 environment variable is set by `@nx/next:server` when running the dev server.
// - The fallback `__dirname` is for production builds. // - The fallback `__dirname` is for production builds.
// - Feel free to change this to suit your needs. // - 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'; const dev = process.env.NODE_ENV === 'development';
// HTTP Server options: // HTTP Server options:

View File

@ -4,7 +4,9 @@
"module": "commonjs", "module": "commonjs",
"noEmit": false, "noEmit": false,
"incremental": true, "incremental": true,
<% if(hasPlugin && compiler === 'tsc') { %>
"tsBuildInfoFile": "<%= offsetFromRoot %>tmp/buildcache/<%= projectRoot %>/server", "tsBuildInfoFile": "<%= offsetFromRoot %>tmp/buildcache/<%= projectRoot %>/server",
<% } %>
"types": [ "types": [
"node" "node"
] ]

View File

@ -4,6 +4,7 @@ import {
installPackagesTask, installPackagesTask,
joinPathFragments, joinPathFragments,
readJson, readJson,
updateJson,
} from '@nx/devkit'; } from '@nx/devkit';
import { swcCliVersion, swcCoreVersion, swcNodeVersion } from './versions'; import { swcCliVersion, swcCoreVersion, swcNodeVersion } from './versions';
import { addSwcConfig } from '@nx/js/src/utils/swc/add-swc-config'; 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']; rootPackageJson.devDependencies?.['@swc/cli'];
if (!tree.exists(swcConfigPath)) { 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) { if (!hasSwcDepedency || !hasSwcCliDependency) {