From 7a4c9697d01881e4e2077710b743498d8ac5ce31 Mon Sep 17 00:00:00 2001
From: Katerina Skroumpelou <sk.katherine@gmail.com>
Date: Tue, 12 Dec 2023 12:09:39 +0200
Subject: [PATCH] fix(vite): better extra args resolution

---
 .../vite/src/executors/build/build.impl.ts    |  6 +-
 .../executors/dev-server/dev-server.impl.ts   | 52 ++++++++++++++---
 .../preview-server/preview-server.impl.ts     | 56 +++++++++++++++----
 packages/vite/src/executors/test/lib/utils.ts |  6 +-
 packages/vite/src/utils/options-utils.ts      | 24 +++-----
 5 files changed, 103 insertions(+), 41 deletions(-)

diff --git a/packages/vite/src/executors/build/build.impl.ts b/packages/vite/src/executors/build/build.impl.ts
index b287d9871c06b..74cafd3c96930 100644
--- a/packages/vite/src/executors/build/build.impl.ts
+++ b/packages/vite/src/executors/build/build.impl.ts
@@ -49,7 +49,7 @@ export async function* viteBuildExecutor(
       ? process.cwd()
       : relative(context.cwd, joinPathFragments(context.root, projectRoot));
 
-  const { buildOptions, otherOptions } = await getExtraArgs(options);
+  const { buildOptions, otherOptions } = await getBuildExtraArgs(options);
 
   const resolved = await loadConfigFromFile(
     {
@@ -188,7 +188,9 @@ export async function* viteBuildExecutor(
   }
 }
 
-async function getExtraArgs(options: ViteBuildExecutorOptions): Promise<{
+export async function getBuildExtraArgs(
+  options: ViteBuildExecutorOptions
+): Promise<{
   buildOptions: BuildOptions;
   otherOptions: Record<string, any>;
 }> {
diff --git a/packages/vite/src/executors/dev-server/dev-server.impl.ts b/packages/vite/src/executors/dev-server/dev-server.impl.ts
index c201b137e109d..53ede5f33a58a 100644
--- a/packages/vite/src/executors/dev-server/dev-server.impl.ts
+++ b/packages/vite/src/executors/dev-server/dev-server.impl.ts
@@ -3,6 +3,7 @@ import {
   loadConfigFromFile,
   type InlineConfig,
   type ViteDevServer,
+  ServerOptions,
 } from 'vite';
 
 import {
@@ -43,11 +44,11 @@ export async function* viteDevServerExecutor(
     projectRoot,
     buildTargetOptions.configFile
   );
-  const extraArgs = await getExtraArgs(options);
+  const { serverOptions, otherOptions } = await getServerExtraArgs(options);
   const resolved = await loadConfigFromFile(
     {
-      mode: extraArgs?.mode ?? 'production',
-      command: 'build',
+      mode: otherOptions?.mode ?? 'development',
+      command: 'serve',
     },
     viteConfigPath
   );
@@ -62,9 +63,9 @@ export async function* viteDevServerExecutor(
     {
       server: {
         ...(await getViteServerOptions(options, context)),
-        ...extraArgs,
+        ...serverOptions,
       },
-      ...extraArgs,
+      ...otherOptions,
     }
   );
 
@@ -111,9 +112,12 @@ async function runViteDevServer(server: ViteDevServer): Promise<void> {
 
 export default viteDevServerExecutor;
 
-async function getExtraArgs(
+async function getServerExtraArgs(
   options: ViteDevServerExecutorOptions
-): Promise<InlineConfig> {
+): Promise<{
+  serverOptions: ServerOptions;
+  otherOptions: Record<string, any>;
+}> {
   // support passing extra args to vite cli
   const schema = await import('./schema.json');
   const extraArgs = {};
@@ -123,5 +127,37 @@ async function getExtraArgs(
     }
   }
 
-  return extraArgs as InlineConfig;
+  const serverOptions = {} as ServerOptions;
+  const serverSchemaKeys = [
+    'hmr',
+    'warmup',
+    'watch',
+    'middlewareMode',
+    'fs',
+    'origin',
+    'preTransformRequests',
+    'sourcemapIgnoreList',
+    'port',
+    'strictPort',
+    'host',
+    'https',
+    'open',
+    'proxy',
+    'cors',
+    'headers',
+  ];
+
+  const otherOptions = {};
+  for (const key of Object.keys(extraArgs)) {
+    if (serverSchemaKeys.includes(key)) {
+      serverOptions[key] = extraArgs[key];
+    } else {
+      otherOptions[key] = extraArgs[key];
+    }
+  }
+
+  return {
+    serverOptions,
+    otherOptions,
+  };
 }
diff --git a/packages/vite/src/executors/preview-server/preview-server.impl.ts b/packages/vite/src/executors/preview-server/preview-server.impl.ts
index 3aef6c52b8b49..c4e400b7529f2 100644
--- a/packages/vite/src/executors/preview-server/preview-server.impl.ts
+++ b/packages/vite/src/executors/preview-server/preview-server.impl.ts
@@ -5,15 +5,16 @@ import {
   parseTargetString,
   runExecutor,
 } from '@nx/devkit';
-import type { InlineConfig, PreviewServer } from 'vite';
+import type { InlineConfig, PreviewOptions, PreviewServer } from 'vite';
 import {
   getNxTargetOptions,
-  getVitePreviewOptions,
+  getProxyConfig,
   normalizeViteConfigFilePath,
 } from '../../utils/options-utils';
 import { ViteBuildExecutorOptions } from '../build/schema';
 import { VitePreviewServerExecutorOptions } from './schema';
 import { relative } from 'path';
+import { getBuildExtraArgs } from '../build/build.impl';
 
 export async function* vitePreviewServerExecutor(
   options: VitePreviewServerExecutorOptions,
@@ -43,15 +44,20 @@ export async function* vitePreviewServerExecutor(
     options.buildTarget,
     context
   );
+
   const viteConfigPath = normalizeViteConfigFilePath(
     context.root,
     projectRoot,
     buildTargetOptions.configFile
   );
-  const extraArgs = await getExtraArgs(options);
+
+  const { buildOptions, otherOptions: otherOptionsFromBuild } =
+    await getBuildExtraArgs(buildTargetOptions);
+
+  const { previewOptions, otherOptions } = await getExtraArgs(options);
   const resolved = await loadConfigFromFile(
     {
-      mode: extraArgs?.mode ?? 'production',
+      mode: otherOptions?.mode ?? 'production',
       command: 'build',
     },
     viteConfigPath
@@ -81,12 +87,16 @@ export async function* vitePreviewServerExecutor(
     ...{ watch: {} },
     build: {
       outDir,
+      ...(isCustomBuildTarget ? {} : buildOptions),
+    },
+    ...(isCustomBuildTarget ? {} : otherOptionsFromBuild),
+    ...otherOptions,
+    preview: {
+      ...getProxyConfig(context, otherOptions.proxyConfig),
+      ...previewOptions,
     },
-    ...(isCustomBuildTarget ? {} : buildTargetOptions),
-    ...extraArgs,
   };
 
-  // Retrieve the server configuration.
   const serverConfig: InlineConfig = mergeConfig(
     {
       // This should not be needed as it's going to be set in vite.config.ts
@@ -96,7 +106,6 @@ export async function* vitePreviewServerExecutor(
     },
     {
       ...mergedOptions,
-      preview: getVitePreviewOptions(mergedOptions, context),
     }
   );
 
@@ -180,7 +189,10 @@ export default vitePreviewServerExecutor;
 
 async function getExtraArgs(
   options: VitePreviewServerExecutorOptions
-): Promise<InlineConfig> {
+): Promise<{
+  previewOptions: PreviewOptions;
+  otherOptions: Record<string, any>;
+}> {
   // support passing extra args to vite cli
   const schema = await import('./schema.json');
   const extraArgs = {};
@@ -190,5 +202,29 @@ async function getExtraArgs(
     }
   }
 
-  return extraArgs as InlineConfig;
+  const previewOptions = {} as PreviewOptions;
+  const previewSchemaKeys = [
+    'port',
+    'strictPort',
+    'host',
+    'https',
+    'open',
+    'proxy',
+    'cors',
+    'headers',
+  ];
+
+  const otherOptions = {};
+  for (const key of Object.keys(extraArgs)) {
+    if (previewSchemaKeys.includes(key)) {
+      previewOptions[key] = extraArgs[key];
+    } else {
+      otherOptions[key] = extraArgs[key];
+    }
+  }
+
+  return {
+    previewOptions,
+    otherOptions,
+  };
 }
diff --git a/packages/vite/src/executors/test/lib/utils.ts b/packages/vite/src/executors/test/lib/utils.ts
index f7a258f661faa..ad2ed8af333f5 100644
--- a/packages/vite/src/executors/test/lib/utils.ts
+++ b/packages/vite/src/executors/test/lib/utils.ts
@@ -7,7 +7,6 @@ import {
 import { VitestExecutorOptions } from '../schema';
 import { normalizeViteConfigFilePath } from '../../../utils/options-utils';
 import { relative } from 'path';
-import { NxReporter } from './nx-reporter';
 
 export async function getOptions(
   options: VitestExecutorOptions,
@@ -76,12 +75,9 @@ export async function getExtraArgs(
   options: VitestExecutorOptions
 ): Promise<Record<string, any>> {
   // support passing extra args to vite cli
-  const schema = await import('../schema.json');
   const extraArgs: Record<string, any> = {};
   for (const key of Object.keys(options)) {
-    if (!schema.properties[key]) {
-      extraArgs[key] = options[key];
-    }
+    extraArgs[key] = options[key];
   }
 
   return extraArgs;
diff --git a/packages/vite/src/utils/options-utils.ts b/packages/vite/src/utils/options-utils.ts
index 07d966969b389..8302f36413afa 100644
--- a/packages/vite/src/utils/options-utils.ts
+++ b/packages/vite/src/utils/options-utils.ts
@@ -6,7 +6,7 @@ import {
   readTargetOptions,
 } from '@nx/devkit';
 import { existsSync } from 'fs';
-import { PreviewOptions, ServerOptions } from 'vite';
+import { ProxyOptions, ServerOptions } from 'vite';
 import { ViteDevServerExecutorOptions } from '../executors/dev-server/schema';
 
 /**
@@ -117,24 +117,16 @@ export async function getViteServerOptions(
   return serverOptions;
 }
 
-/**
- * Builds the options for the vite preview server.
- */
-export function getVitePreviewOptions(
-  options: Record<string, any>,
-  context: ExecutorContext
-): PreviewOptions {
-  const serverOptions: ServerOptions = {};
-  const proxyConfigPath = getViteServerProxyConfigPath(
-    options.proxyConfig,
-    context
-  );
+export function getProxyConfig(
+  context: ExecutorContext,
+  proxyConfig?: string
+): Record<string, string | ProxyOptions> | undefined {
+  const proxyConfigPath = getViteServerProxyConfigPath(proxyConfig, context);
   if (proxyConfigPath) {
     logger.info(`Loading proxy configuration from: ${proxyConfigPath}`);
-    serverOptions.proxy = require(proxyConfigPath);
+    return require(proxyConfigPath);
   }
-
-  return serverOptions;
+  return;
 }
 
 export function getNxTargetOptions(target: string, context: ExecutorContext) {