Skip to content

Commit

Permalink
feat(nextjs): Use next.js cli for build and serve targets
Browse files Browse the repository at this point in the history
  • Loading branch information
ndcunningham committed May 9, 2023
1 parent 8347e61 commit e6e4218
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 104 deletions.
4 changes: 4 additions & 0 deletions docs/generated/packages/next/executors/server.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
"type": "boolean",
"description": "Read buildable libraries from source instead of building them separately.",
"default": true
},
"keepAliveTimeout": {
"type": "number",
"description": "Max milliseconds to wait before closing inactive connection."
}
},
"required": ["buildTarget"],
Expand Down
21 changes: 13 additions & 8 deletions packages/next/src/executors/build/build.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
readJsonFile,
workspaceRoot,
writeJsonFile,
logger,
} from '@nx/devkit';
import { createLockFile, createPackageJson, getLockFileName } from '@nx/js';
import build from 'next/dist/build';
Expand All @@ -17,6 +18,7 @@ import { updatePackageJson } from './lib/update-package-json';
import { createNextConfigFile } from './lib/create-next-config-file';
import { checkPublicDirectory } from './lib/check-project';
import { NextBuildBuilderOptions } from '../../utils/types';
import { ExecSyncOptions, execSync } from 'child_process';

export default async function buildExecutor(
options: NextBuildBuilderOptions,
Expand Down Expand Up @@ -50,14 +52,17 @@ export default async function buildExecutor(

const debug = !!process.env.NX_VERBOSE_LOGGING || options.debug;

// Check the major and minor version numbers
if (lt(nextVersion, '13.2.0')) {
// If the version is lower than 13.2.0, use the second parameter as the config object
await build(root, null, false, debug);
} else {
// Otherwise, use the third parameter as a boolean flag for verbose logging
// @ts-ignore
await build(root, false, debug);
const command = `next build ${debug ? '--verbose' : ''}`;
const execSyncOptions: ExecSyncOptions = {
stdio: 'inherit',
encoding: 'utf-8',
cwd: root,
};
try {
execSync(command, execSyncOptions);
} catch (error) {
logger.error(`Error occurred while trying to run the ${command}`);
logger.error(error);
}

if (!directoryExists(options.outputPath)) {
Expand Down
7 changes: 5 additions & 2 deletions packages/next/src/executors/export/export.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
NextBuildBuilderOptions,
NextExportBuilderOptions,
} from '../../utils/types';
import { PHASE_EXPORT } from '../../utils/constants';

import nextTrace = require('next/dist/trace');
import { platform } from 'os';
import { execFileSync } from 'child_process';
Expand All @@ -25,6 +25,10 @@ import * as chalk from 'chalk';
// platform specific command name
const pmCmd = platform() === 'win32' ? `npx.cmd` : 'npx';

/**
* @deprecated use output inside of your next.config.js
* Read https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
**/
export default async function exportExecutor(
options: NextExportBuilderOptions,
context: ExecutorContext
Expand All @@ -41,7 +45,6 @@ export default async function exportExecutor(
dependencies = result.dependencies;
}

const libsDir = join(context.root, workspaceLayout().libsDir);
const buildTarget = parseTargetString(
options.buildTarget,
context.projectGraph
Expand Down
67 changes: 67 additions & 0 deletions packages/next/src/executors/server/custom-server.impl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'dotenv/config';
import {
ExecutorContext,
parseTargetString,
readTargetOptions,
runExecutor,
} from '@nx/devkit';
import { join, resolve } from 'path';

import {
NextBuildBuilderOptions,
NextServeBuilderOptions,
} from '../../utils/types';

export default async function* serveExecutor(
options: NextServeBuilderOptions,
context: ExecutorContext
) {
// Cast to any to overwrite NODE_ENV
(process.env as any).NODE_ENV = process.env.NODE_ENV
? process.env.NODE_ENV
: options.dev
? 'development'
: 'production';

// Setting port that the custom server should use.
(process.env as any).PORT = options.port;

const buildOptions = readTargetOptions<NextBuildBuilderOptions>(
parseTargetString(options.buildTarget, context.projectGraph),
context
);
const root = resolve(context.root, buildOptions.root);

yield* runCustomServer(root, options, context);
}

async function* runCustomServer(
root: string,
options: NextServeBuilderOptions,
context: ExecutorContext
) {
process.env.NX_NEXT_DIR = root;
process.env.NX_NEXT_PUBLIC_DIR = join(root, 'public');

const baseUrl = `http://${options.hostname || 'localhost'}:${options.port}`;

const customServerBuild = await runExecutor(
parseTargetString(options.customServerTarget, context.projectGraph),
{
watch: options.dev ? true : false,
},
context
);

for await (const result of customServerBuild) {
if (!result.success) {
return result;
}
yield {
success: true,
baseUrl,
};
}

return { success: true };
}
4 changes: 4 additions & 0 deletions packages/next/src/executors/server/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
"type": "boolean",
"description": "Read buildable libraries from source instead of building them separately.",
"default": true
},
"keepAliveTimeout": {
"type": "number",
"description": "Max milliseconds to wait before closing inactive connection."
}
},
"required": ["buildTarget"]
Expand Down
124 changes: 30 additions & 94 deletions packages/next/src/executors/server/server.impl.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
import 'dotenv/config';
import {
ExecutorContext,
logger,
parseTargetString,
readTargetOptions,
runExecutor,
} from '@nx/devkit';
import * as chalk from 'chalk';
import { existsSync } from 'fs';
import { join, resolve } from 'path';
import { resolve } from 'path';

import {
NextBuildBuilderOptions,
NextServeBuilderOptions,
NextServerOptions,
ProxyConfig,
} from '../../utils/types';
import { defaultServer } from './lib/default-server';
import { spawn } from 'child_process';
import customServer from './custom-server.impl';

export default async function* serveExecutor(
options: NextServeBuilderOptions,
context: ExecutorContext
) {
if (options.customServerTarget) {
return yield* customServer(options, context);
}
// Cast to any to overwrite NODE_ENV
(process.env as any).NODE_ENV = process.env.NODE_ENV
? process.env.NODE_ENV
Expand All @@ -38,96 +36,34 @@ export default async function* serveExecutor(
);
const root = resolve(context.root, buildOptions.root);

if (options.customServerTarget) {
yield* runCustomServer(root, options, buildOptions, context);
} else {
yield* runNextDevServer(root, options, context);
}
}
const { port, keepAliveTimeout, hostname } = options;

async function* runNextDevServer(
root: string,
options: NextServeBuilderOptions,
context: ExecutorContext
) {
const baseUrl = `http://${options.hostname || 'localhost'}:${options.port}`;
const settings: NextServerOptions = {
dev: options.dev,
dir: root,
staticMarkup: options.staticMarkup,
quiet: options.quiet,
port: options.port,
customServer: !!options.customServerTarget,
hostname: options.hostname || 'localhost',
};

// look for the proxy.conf.json
let proxyConfig: ProxyConfig;
const proxyConfigPath = options.proxyConfig
? join(context.root, options.proxyConfig)
: join(root, 'proxy.conf.json');

// TODO(v16): Remove proxy support.
if (existsSync(proxyConfigPath)) {
logger.warn(
`The "proxyConfig" option will be removed in Nx 16. Use the "rewrites" feature from Next.js instead. See: https://nextjs.org/docs/api-reference/next.config.js/rewrites`
);
proxyConfig = require(proxyConfigPath);
}
const args = formatObjectToCli({ port, keepAliveTimeout, hostname });
const nextDir = resolve(context.root, buildOptions.outputPath);

try {
await defaultServer(settings, proxyConfig);
logger.info(`[ ${chalk.green('ready')} ] on ${baseUrl}`);
const command = `next ${
options.dev ? `dev ${args}` : `start ${nextDir} ${args}`
}`;

yield {
baseUrl,
success: true,
};
return yield new Promise<{ success: boolean }>((res, rej) => {
const server = spawn(command, {
cwd: options.dev ? root : nextDir,
stdio: 'inherit',
shell: true,
});

// This Promise intentionally never resolves, leaving the process running
await new Promise<{ success: boolean }>(() => {});
} catch (e) {
if (options.dev) {
throw e;
} else {
if (process.env.NX_VERBOSE_LOGGING) {
console.error(e);
}
throw new Error(
`Could not start production server. Try building your app with \`nx build ${context.projectName}\`.`
);
}
}
server.on('exit', (code) => {
code != 0 ? rej({ success: false }) : res({ success: true });
});
});
}

async function* runCustomServer(
root: string,
options: NextServeBuilderOptions,
buildOptions: NextBuildBuilderOptions,
context: ExecutorContext
) {
process.env.NX_NEXT_DIR = root;
process.env.NX_NEXT_PUBLIC_DIR = join(root, 'public');

const baseUrl = `http://${options.hostname || 'localhost'}:${options.port}`;

const customServerBuild = await runExecutor(
parseTargetString(options.customServerTarget, context.projectGraph),
{
watch: options.dev ? true : false,
},
context
);

for await (const result of customServerBuild) {
if (!result.success) {
return result;
}
yield {
success: true,
baseUrl,
};
}

return { success: true };
function formatObjectToCli(obj: { [key: string]: any }): string {
return Object.entries(obj)
.reduce(
(arr, [key, value]) =>
value !== undefined ? `${arr}--${key}=${value} ` : arr,
''
)
.trimEnd();
}
1 change: 1 addition & 0 deletions packages/next/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface NextServeBuilderOptions {
hostname?: string;
proxyConfig?: string;
buildLibsFromSource?: boolean;
keepAliveTimeout?: number;
}

export interface NextExportBuilderOptions {
Expand Down

0 comments on commit e6e4218

Please sign in to comment.