feat(nextjs): Use next.js cli for build and serve targets (#16896)
This commit is contained in:
parent
7de80ddb62
commit
3d76d95b29
@ -68,8 +68,15 @@
|
|||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Enable Next.js debug build logging",
|
"description": "Enable Next.js debug build logging"
|
||||||
"default": false
|
},
|
||||||
|
"profile": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Used to enable React Production Profiling"
|
||||||
|
},
|
||||||
|
"experimentalAppOnly": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Only build 'app' routes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["root", "outputPath"],
|
"required": ["root", "outputPath"],
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
"presets": []
|
"presets": []
|
||||||
},
|
},
|
||||||
"description": "Export a Next.js application. The exported application is located at `dist/$outputPath/exported`.",
|
"description": "Export a Next.js application. The exported application is located at `dist/$outputPath/exported`.",
|
||||||
|
"x-deprecated": "Use static exports in next.config.js instead. See: https://nextjs.org/docs/pages/building-your-application/deploying/static-exports.",
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/next/src/executors/export/schema.json",
|
"path": "/packages/next/src/executors/export/schema.json",
|
||||||
|
|||||||
@ -53,6 +53,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Read buildable libraries from source instead of building them separately.",
|
"description": "Read buildable libraries from source instead of building them separately.",
|
||||||
"default": true
|
"default": true
|
||||||
|
},
|
||||||
|
"keepAliveTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Max milliseconds to wait before closing inactive connection."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["buildTarget"],
|
"required": ["buildTarget"],
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
|
import { execSync } from 'child_process';
|
||||||
import {
|
import {
|
||||||
checkFilesExist,
|
checkFilesExist,
|
||||||
killPorts,
|
killPort,
|
||||||
readJson,
|
readJson,
|
||||||
runCLI,
|
runCLI,
|
||||||
runCLIAsync,
|
runCLIAsync,
|
||||||
@ -42,10 +43,10 @@ export async function checkApp(
|
|||||||
|
|
||||||
if (opts.checkE2E && runCypressTests()) {
|
if (opts.checkE2E && runCypressTests()) {
|
||||||
const e2eResults = runCLI(
|
const e2eResults = runCLI(
|
||||||
`e2e ${appName}-e2e --no-watch --configuration=production`
|
`e2e ${appName}-e2e --no-watch --configuration=production --port=9000`
|
||||||
);
|
);
|
||||||
expect(e2eResults).toContain('All specs passed!');
|
expect(e2eResults).toContain('All specs passed!');
|
||||||
expect(await killPorts()).toBeTruthy();
|
await killPort(9000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.checkExport) {
|
if (opts.checkExport) {
|
||||||
|
|||||||
@ -13,7 +13,8 @@
|
|||||||
"export": {
|
"export": {
|
||||||
"implementation": "./src/executors/export/export.impl",
|
"implementation": "./src/executors/export/export.impl",
|
||||||
"schema": "./src/executors/export/schema.json",
|
"schema": "./src/executors/export/schema.json",
|
||||||
"description": "Export a Next.js application. The exported application is located at `dist/$outputPath/exported`."
|
"description": "Export a Next.js application. The exported application is located at `dist/$outputPath/exported`.",
|
||||||
|
"x-deprecated": "Use static exports in next.config.js instead. See: https://nextjs.org/docs/pages/building-your-application/deploying/static-exports."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"builders": {
|
"builders": {
|
||||||
@ -30,7 +31,8 @@
|
|||||||
"export": {
|
"export": {
|
||||||
"implementation": "./src/executors/export/compat",
|
"implementation": "./src/executors/export/compat",
|
||||||
"schema": "./src/executors/export/schema.json",
|
"schema": "./src/executors/export/schema.json",
|
||||||
"description": "Export a Next.js application. The exported application is located at `dist/$outputPath/exported`."
|
"description": "Export a Next.js application. The exported application is located at `dist/$outputPath/exported`.",
|
||||||
|
"x-deprecated": "Use static exports in next.config.js instead. See: https://nextjs.org/docs/pages/building-your-application/deploying/static-exports."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
"migrations": "./migrations.json"
|
"migrations": "./migrations.json"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"next": "^13.0.0"
|
"next": ">=13.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-decorators": "^7.14.5",
|
"@babel/plugin-proposal-decorators": "^7.14.5",
|
||||||
|
|||||||
@ -113,55 +113,10 @@ function getNxContext(
|
|||||||
/**
|
/**
|
||||||
* Try to read output dir from project, and default to '.next' if executing outside of Nx (e.g. dist is added to a docker image).
|
* Try to read output dir from project, and default to '.next' if executing outside of Nx (e.g. dist is added to a docker image).
|
||||||
*/
|
*/
|
||||||
async function determineDistDirForProdServer(
|
|
||||||
nextConfig: NextConfig
|
|
||||||
): Promise<string> {
|
|
||||||
const project = process.env.NX_TASK_TARGET_PROJECT;
|
|
||||||
const target = process.env.NX_TASK_TARGET_TARGET;
|
|
||||||
const configuration = process.env.NX_TASK_TARGET_CONFIGURATION;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (project && target) {
|
|
||||||
// If NX env vars are set, then devkit must be available.
|
|
||||||
const {
|
|
||||||
createProjectGraphAsync,
|
|
||||||
joinPathFragments,
|
|
||||||
offsetFromRoot,
|
|
||||||
} = require('@nx/devkit');
|
|
||||||
const originalTarget = { project, target, configuration };
|
|
||||||
const graph = await createProjectGraphAsync();
|
|
||||||
|
|
||||||
const { options, node: projectNode } = getNxContext(
|
|
||||||
graph,
|
|
||||||
originalTarget
|
|
||||||
);
|
|
||||||
const outputDir = `${offsetFromRoot(projectNode.data.root)}${
|
|
||||||
options.outputPath
|
|
||||||
}`;
|
|
||||||
return nextConfig.distDir && nextConfig.distDir !== '.next'
|
|
||||||
? joinPathFragments(outputDir, nextConfig.distDir)
|
|
||||||
: joinPathFragments(outputDir, '.next');
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignored -- fallback to Next.js default of '.next'
|
|
||||||
}
|
|
||||||
|
|
||||||
return nextConfig.distDir || '.next';
|
|
||||||
}
|
|
||||||
|
|
||||||
function withNx(
|
function withNx(
|
||||||
_nextConfig = {} as WithNxOptions,
|
_nextConfig = {} as WithNxOptions,
|
||||||
context: WithNxContext = getWithNxContext()
|
context: WithNxContext = getWithNxContext()
|
||||||
): NextConfigFn {
|
): NextConfigFn {
|
||||||
// If this is not set user will see compile errors in Next.js 13.4.
|
|
||||||
// See: https://github.com/nrwl/nx/issues/16692, https://github.com/vercel/next.js/issues/49169
|
|
||||||
// TODO(jack): Remove this once Nx is refactored to invoke CLI directly.
|
|
||||||
forNextVersion('>=13.4.0', () => {
|
|
||||||
process.env['__NEXT_PRIVATE_PREBUNDLED_REACT'] =
|
|
||||||
// Not in Next 13.3 or earlier, so need to access config via string
|
|
||||||
_nextConfig.experimental?.['serverActions'] ? 'experimental' : 'next';
|
|
||||||
});
|
|
||||||
|
|
||||||
return async (phase: string) => {
|
return async (phase: string) => {
|
||||||
const { PHASE_PRODUCTION_SERVER } = await import('next/constants');
|
const { PHASE_PRODUCTION_SERVER } = await import('next/constants');
|
||||||
if (phase === PHASE_PRODUCTION_SERVER) {
|
if (phase === PHASE_PRODUCTION_SERVER) {
|
||||||
@ -169,8 +124,8 @@ function withNx(
|
|||||||
// NOTE: Avoid any `require(...)` or `import(...)` statements here. Development dependencies are not available at production runtime.
|
// NOTE: Avoid any `require(...)` or `import(...)` statements here. Development dependencies are not available at production runtime.
|
||||||
const { nx, ...validNextConfig } = _nextConfig;
|
const { nx, ...validNextConfig } = _nextConfig;
|
||||||
return {
|
return {
|
||||||
|
distDir: '.next',
|
||||||
...validNextConfig,
|
...validNextConfig,
|
||||||
distDir: await determineDistDirForProdServer(_nextConfig),
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import {
|
|||||||
readJsonFile,
|
readJsonFile,
|
||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
writeJsonFile,
|
writeJsonFile,
|
||||||
|
logger,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { createLockFile, createPackageJson, getLockFileName } from '@nx/js';
|
import { createLockFile, createPackageJson, getLockFileName } from '@nx/js';
|
||||||
import build from 'next/dist/build';
|
|
||||||
import { join, resolve } from 'path';
|
import { join, resolve } from 'path';
|
||||||
import { copySync, existsSync, mkdir, writeFileSync } from 'fs-extra';
|
import { copySync, existsSync, mkdir, writeFileSync } from 'fs-extra';
|
||||||
import { lt, gte } from 'semver';
|
import { lt, gte } from 'semver';
|
||||||
@ -17,6 +17,8 @@ import { updatePackageJson } from './lib/update-package-json';
|
|||||||
import { createNextConfigFile } from './lib/create-next-config-file';
|
import { createNextConfigFile } from './lib/create-next-config-file';
|
||||||
import { checkPublicDirectory } from './lib/check-project';
|
import { checkPublicDirectory } from './lib/check-project';
|
||||||
import { NextBuildBuilderOptions } from '../../utils/types';
|
import { NextBuildBuilderOptions } from '../../utils/types';
|
||||||
|
import { ExecSyncOptions, execSync } from 'child_process';
|
||||||
|
import { createCliOptions } from '../../utils/create-cli-options';
|
||||||
|
|
||||||
export default async function buildExecutor(
|
export default async function buildExecutor(
|
||||||
options: NextBuildBuilderOptions,
|
options: NextBuildBuilderOptions,
|
||||||
@ -42,22 +44,24 @@ export default async function buildExecutor(
|
|||||||
reactDomVersion &&
|
reactDomVersion &&
|
||||||
gte(checkAndCleanWithSemver('react-dom', reactDomVersion), '18.0.0');
|
gte(checkAndCleanWithSemver('react-dom', reactDomVersion), '18.0.0');
|
||||||
if (hasReact18) {
|
if (hasReact18) {
|
||||||
(process.env as any).__NEXT_REACT_ROOT ||= 'true';
|
process.env['__NEXT_REACT_ROOT'] ||= 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the installed Next.js version (will be removed after Nx 16 and Next.js update)
|
const { experimentalAppOnly, profile, debug } = options;
|
||||||
const nextVersion = require('next/package.json').version;
|
|
||||||
|
|
||||||
const debug = !!process.env.NX_VERBOSE_LOGGING || options.debug;
|
const args = createCliOptions({ experimentalAppOnly, profile, debug });
|
||||||
|
const command = `npx next build ${args}`;
|
||||||
// Check the major and minor version numbers
|
const execSyncOptions: ExecSyncOptions = {
|
||||||
if (lt(nextVersion, '13.2.0')) {
|
stdio: 'inherit',
|
||||||
// If the version is lower than 13.2.0, use the second parameter as the config object
|
encoding: 'utf-8',
|
||||||
await build(root, null, false, debug);
|
cwd: root,
|
||||||
} else {
|
};
|
||||||
// Otherwise, use the third parameter as a boolean flag for verbose logging
|
try {
|
||||||
// @ts-ignore
|
execSync(command, execSyncOptions);
|
||||||
await build(root, false, debug);
|
} catch (error) {
|
||||||
|
logger.error(`Error occurred while trying to run the ${command}`);
|
||||||
|
logger.error(error);
|
||||||
|
return { success: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!directoryExists(options.outputPath)) {
|
if (!directoryExists(options.outputPath)) {
|
||||||
|
|||||||
@ -65,8 +65,15 @@
|
|||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Enable Next.js debug build logging",
|
"description": "Enable Next.js debug build logging"
|
||||||
"default": false
|
},
|
||||||
|
"profile": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Used to enable React Production Profiling"
|
||||||
|
},
|
||||||
|
"experimentalAppOnly": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Only build 'app' routes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["root", "outputPath"]
|
"required": ["root", "outputPath"]
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import {
|
|||||||
NextBuildBuilderOptions,
|
NextBuildBuilderOptions,
|
||||||
NextExportBuilderOptions,
|
NextExportBuilderOptions,
|
||||||
} from '../../utils/types';
|
} from '../../utils/types';
|
||||||
import { PHASE_EXPORT } from '../../utils/constants';
|
|
||||||
import nextTrace = require('next/dist/trace');
|
import nextTrace = require('next/dist/trace');
|
||||||
import { platform } from 'os';
|
import { platform } from 'os';
|
||||||
import { execFileSync } from 'child_process';
|
import { execFileSync } from 'child_process';
|
||||||
@ -25,6 +25,18 @@ import * as chalk from 'chalk';
|
|||||||
// platform specific command name
|
// platform specific command name
|
||||||
const pmCmd = platform() === 'win32' ? `npx.cmd` : 'npx';
|
const pmCmd = platform() === 'win32' ? `npx.cmd` : 'npx';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use output inside of your next.config.js
|
||||||
|
* Example
|
||||||
|
* const nextConfig = {
|
||||||
|
nx: {
|
||||||
|
svgr: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
output: 'export'
|
||||||
|
};
|
||||||
|
* Read https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
|
||||||
|
**/
|
||||||
export default async function exportExecutor(
|
export default async function exportExecutor(
|
||||||
options: NextExportBuilderOptions,
|
options: NextExportBuilderOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
@ -41,7 +53,6 @@ export default async function exportExecutor(
|
|||||||
dependencies = result.dependencies;
|
dependencies = result.dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
const libsDir = join(context.root, workspaceLayout().libsDir);
|
|
||||||
const buildTarget = parseTargetString(
|
const buildTarget = parseTargetString(
|
||||||
options.buildTarget,
|
options.buildTarget,
|
||||||
context.projectGraph
|
context.projectGraph
|
||||||
|
|||||||
67
packages/next/src/executors/server/custom-server.impl.ts
Normal file
67
packages/next/src/executors/server/custom-server.impl.ts
Normal file
@ -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 };
|
||||||
|
}
|
||||||
@ -50,6 +50,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Read buildable libraries from source instead of building them separately.",
|
"description": "Read buildable libraries from source instead of building them separately.",
|
||||||
"default": true
|
"default": true
|
||||||
|
},
|
||||||
|
"keepAliveTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Max milliseconds to wait before closing inactive connection."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["buildTarget"]
|
"required": ["buildTarget"]
|
||||||
|
|||||||
@ -1,27 +1,29 @@
|
|||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
|
import * as net from 'net';
|
||||||
import {
|
import {
|
||||||
ExecutorContext,
|
ExecutorContext,
|
||||||
logger,
|
logger,
|
||||||
parseTargetString,
|
parseTargetString,
|
||||||
readTargetOptions,
|
readTargetOptions,
|
||||||
runExecutor,
|
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import * as chalk from 'chalk';
|
import { resolve } from 'path';
|
||||||
import { existsSync } from 'fs';
|
|
||||||
import { join, resolve } from 'path';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
NextBuildBuilderOptions,
|
NextBuildBuilderOptions,
|
||||||
NextServeBuilderOptions,
|
NextServeBuilderOptions,
|
||||||
NextServerOptions,
|
|
||||||
ProxyConfig,
|
|
||||||
} from '../../utils/types';
|
} from '../../utils/types';
|
||||||
import { defaultServer } from './lib/default-server';
|
import { spawn } from 'child_process';
|
||||||
|
import customServer from './custom-server.impl';
|
||||||
|
import { createCliOptions } from '../../utils/create-cli-options';
|
||||||
|
import { createAsyncIterable } from '@nx/devkit/src/utils/async-iterable';
|
||||||
|
|
||||||
export default async function* serveExecutor(
|
export default async function* serveExecutor(
|
||||||
options: NextServeBuilderOptions,
|
options: NextServeBuilderOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
) {
|
) {
|
||||||
|
if (options.customServerTarget) {
|
||||||
|
return yield* customServer(options, context);
|
||||||
|
}
|
||||||
// Cast to any to overwrite NODE_ENV
|
// Cast to any to overwrite NODE_ENV
|
||||||
(process.env as any).NODE_ENV = process.env.NODE_ENV
|
(process.env as any).NODE_ENV = process.env.NODE_ENV
|
||||||
? process.env.NODE_ENV
|
? process.env.NODE_ENV
|
||||||
@ -38,96 +40,74 @@ export default async function* serveExecutor(
|
|||||||
);
|
);
|
||||||
const root = resolve(context.root, buildOptions.root);
|
const root = resolve(context.root, buildOptions.root);
|
||||||
|
|
||||||
if (options.customServerTarget) {
|
const { port, keepAliveTimeout, hostname } = options;
|
||||||
yield* runCustomServer(root, options, buildOptions, context);
|
|
||||||
} else {
|
|
||||||
yield* runNextDevServer(root, options, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function* runNextDevServer(
|
const args = createCliOptions({ port, keepAliveTimeout, hostname });
|
||||||
root: string,
|
const nextDir = resolve(context.root, buildOptions.outputPath);
|
||||||
options: NextServeBuilderOptions,
|
|
||||||
context: ExecutorContext
|
const command = `npx next ${options.dev ? `dev ${args}` : `start ${args}`}`;
|
||||||
) {
|
|
||||||
const baseUrl = `http://${options.hostname || 'localhost'}:${options.port}`;
|
yield* createAsyncIterable<{ success: boolean; baseUrl: string }>(
|
||||||
const settings: NextServerOptions = {
|
({ done, next, error }) => {
|
||||||
dev: options.dev,
|
// Client to check if server is ready.
|
||||||
dir: root,
|
const client = new net.Socket();
|
||||||
staticMarkup: options.staticMarkup,
|
const cleanupClient = () => {
|
||||||
quiet: options.quiet,
|
client.removeAllListeners('connect');
|
||||||
port: options.port,
|
client.removeAllListeners('error');
|
||||||
customServer: !!options.customServerTarget,
|
client.end();
|
||||||
hostname: options.hostname || 'localhost',
|
client.destroy();
|
||||||
|
client.unref();
|
||||||
};
|
};
|
||||||
|
|
||||||
// look for the proxy.conf.json
|
const waitForServerReady = (retries = 30) => {
|
||||||
let proxyConfig: ProxyConfig;
|
const allowedErrorCodes = ['ECONNREFUSED', 'ECONNRESET'];
|
||||||
const proxyConfigPath = options.proxyConfig
|
|
||||||
? join(context.root, options.proxyConfig)
|
|
||||||
: join(root, 'proxy.conf.json');
|
|
||||||
|
|
||||||
// TODO(v16): Remove proxy support.
|
client.once('connect', () => {
|
||||||
if (existsSync(proxyConfigPath)) {
|
cleanupClient();
|
||||||
logger.warn(
|
next({
|
||||||
`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);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await defaultServer(settings, proxyConfig);
|
|
||||||
logger.info(`[ ${chalk.green('ready')} ] on ${baseUrl}`);
|
|
||||||
|
|
||||||
yield {
|
|
||||||
baseUrl,
|
|
||||||
success: true,
|
success: true,
|
||||||
};
|
baseUrl: `http://${options.hostname ?? 'localhost'}:${port}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// This Promise intentionally never resolves, leaving the process running
|
client.on('error', (err) => {
|
||||||
await new Promise<{ success: boolean }>(() => {});
|
if (retries === 0 || !allowedErrorCodes.includes(err['code'])) {
|
||||||
} catch (e) {
|
cleanupClient();
|
||||||
if (options.dev) {
|
error(err);
|
||||||
throw e;
|
|
||||||
} else {
|
} else {
|
||||||
if (process.env.NX_VERBOSE_LOGGING) {
|
setTimeout(() => waitForServerReady(retries - 1), 1000);
|
||||||
console.error(e);
|
|
||||||
}
|
}
|
||||||
throw new Error(
|
});
|
||||||
`Could not start production server. Try building your app with \`nx build ${context.projectName}\`.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function* runCustomServer(
|
client.connect({ port, host: '127.0.0.1' });
|
||||||
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 };
|
const server = spawn(command, {
|
||||||
|
cwd: options.dev ? root : nextDir,
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
waitForServerReady();
|
||||||
|
|
||||||
|
server.once('exit', (code) => {
|
||||||
|
cleanupClient();
|
||||||
|
if (code === 0) {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
error(new Error(`Next.js app exited with code ${code}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('exit', async (code) => {
|
||||||
|
if (code === 128 + 2) {
|
||||||
|
server.kill('SIGINT');
|
||||||
|
} else if (code === 128 + 1) {
|
||||||
|
server.kill('SIGHUP');
|
||||||
|
} else {
|
||||||
|
server.kill('SIGTERM');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export function createWebpackConfig(
|
|||||||
): Configuration {
|
): Configuration {
|
||||||
const mainFields = ['es2015', 'module', 'main'];
|
const mainFields = ['es2015', 'module', 'main'];
|
||||||
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
|
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
|
||||||
let tsConfigPath = join(projectRoot, 'tsconfig.json');
|
let tsConfigPath = join(workspaceRoot, projectRoot, 'tsconfig.json');
|
||||||
if (dependencies.length > 0) {
|
if (dependencies.length > 0) {
|
||||||
tsConfigPath = createTmpTsConfig(
|
tsConfigPath = createTmpTsConfig(
|
||||||
join(workspaceRoot, tsConfigPath),
|
join(workspaceRoot, tsConfigPath),
|
||||||
|
|||||||
12
packages/next/src/utils/create-cli-options.ts
Normal file
12
packages/next/src/utils/create-cli-options.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export function createCliOptions(obj: { [key: string]: any }): string {
|
||||||
|
return Object.entries(obj)
|
||||||
|
.reduce((arr, [key, value]) => {
|
||||||
|
if (value !== undefined) {
|
||||||
|
const kebabCase = key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
|
||||||
|
return `${arr}--${kebabCase}=${value} `;
|
||||||
|
} else {
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
}, '')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
@ -38,6 +38,8 @@ export interface NextBuildBuilderOptions {
|
|||||||
generateLockfile?: boolean;
|
generateLockfile?: boolean;
|
||||||
watch?: boolean;
|
watch?: boolean;
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
|
profile?: boolean;
|
||||||
|
experimentalAppOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NextServeBuilderOptions {
|
export interface NextServeBuilderOptions {
|
||||||
@ -50,6 +52,7 @@ export interface NextServeBuilderOptions {
|
|||||||
hostname?: string;
|
hostname?: string;
|
||||||
proxyConfig?: string;
|
proxyConfig?: string;
|
||||||
buildLibsFromSource?: boolean;
|
buildLibsFromSource?: boolean;
|
||||||
|
keepAliveTimeout?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NextExportBuilderOptions {
|
export interface NextExportBuilderOptions {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user