Repository: vercel/next.js
Pull request: Turbopack: fix passing project options from napi

### What?

Make stuff more type safe. Use destructuring to avoid forgetting fields 

Changed files:
filename: crates/napi/src/next_api/project.rs
patch: @@ -137,8 +137,8 @@ pub struct NapiProjectOptions {
     /// Unix path. E.g. `apps/my-app`
     pub project_path: RcStr,
 
-    /// A path where to emit the build outputs, relative to [`Project::project_path`], always Unix
-    /// path. Corresponds to next.config.js's `distDir`.
+    /// A path where tracing output will be written to and/or cache is read/written.
+    /// Usually equal to the `distDir` in next.config.js.
     /// E.g. `.next`
     pub dist_dir: RcStr,
 
@@ -192,11 +192,6 @@ pub struct NapiPartialProjectOptions {
     /// E.g. `apps/my-app`
     pub project_path: Option<RcStr>,
 
-    /// A path where to emit the build outputs, relative to [`Project::project_path`], always a
-    /// Unix path. Corresponds to next.config.js's `distDir`.
-    /// E.g. `.next`
-    pub dist_dir: Option<Option<RcStr>>,
-
     /// Filesystem watcher options.
     pub watch: Option<NapiWatchOptions>,
 
@@ -267,43 +262,70 @@ impl From<NapiWatchOptions> for WatchOptions {
 
 impl From<NapiProjectOptions> for ProjectOptions {
     fn from(val: NapiProjectOptions) -> Self {
+        let NapiProjectOptions {
+            root_path,
+            project_path,
+            // Only used for initializing cache and tracing
+            dist_dir: _,
+            watch,
+            next_config,
+            env,
+            define_env,
+            dev,
+            encryption_key,
+            build_id,
+            preview_props,
+            browserslist_query,
+            no_mangling,
+            current_node_js_version,
+        } = val;
         ProjectOptions {
-            root_path: val.root_path,
-            project_path: val.project_path,
-            watch: val.watch.into(),
-            next_config: val.next_config,
-            env: val
-                .env
-                .into_iter()
-                .map(|var| (var.name, var.value))
-                .collect(),
-            define_env: val.define_env.into(),
-            dev: val.dev,
-            encryption_key: val.encryption_key,
-            build_id: val.build_id,
-            preview_props: val.preview_props.into(),
-            browserslist_query: val.browserslist_query,
-            no_mangling: val.no_mangling,
-            current_node_js_version: val.current_node_js_version,
+            root_path,
+            project_path,
+            watch: watch.into(),
+            next_config,
+            env: env.into_iter().map(|var| (var.name, var.value)).collect(),
+            define_env: define_env.into(),
+            dev,
+            encryption_key,
+            build_id,
+            preview_props: preview_props.into(),
+            browserslist_query,
+            no_mangling,
+            current_node_js_version,
         }
     }
 }
 
 impl From<NapiPartialProjectOptions> for PartialProjectOptions {
     fn from(val: NapiPartialProjectOptions) -> Self {
+        let NapiPartialProjectOptions {
+            root_path,
+            project_path,
+            watch,
+            next_config,
+            env,
+            define_env,
+            dev,
+            encryption_key,
+            build_id,
+            preview_props,
+            browserslist_query,
+            no_mangling,
+        } = val;
         PartialProjectOptions {
-            root_path: val.root_path,
-            project_path: val.project_path,
-            watch: val.watch.map(From::from),
-            next_config: val.next_config,
-            env: val
-                .env
-                .map(|env| env.into_iter().map(|var| (var.name, var.value)).collect()),
-            define_env: val.define_env.map(|env| env.into()),
-            dev: val.dev,
-            encryption_key: val.encryption_key,
-            build_id: val.build_id,
-            preview_props: val.preview_props.map(|props| props.into()),
+            root_path,
+            project_path,
+            watch: watch.map(From::from),
+            next_config,
+            env: env.map(|env| env.into_iter().map(|var| (var.name, var.value)).collect()),
+            define_env: define_env.map(|env| env.into()),
+            dev,
+            encryption_key,
+            build_id,
+            preview_props: preview_props.map(|props| props.into()),
+            browserslist_query,
+            no_mangling,
         }
     }
 }

filename: crates/next-api/src/project.rs
patch: @@ -229,6 +229,14 @@ pub struct PartialProjectOptions {
 
     /// Options for draft mode.
     pub preview_props: Option<DraftModeOptions>,
+
+    /// The browserslist query to use for targeting browsers.
+    pub browserslist_query: Option<RcStr>,
+
+    /// When the code is minified, this opts out of the default mangling of
+    /// local names for variables, functions etc., which can be useful for
+    /// debugging/profiling purposes.
+    pub no_mangling: Option<bool>,
 }
 
 #[derive(
@@ -347,6 +355,8 @@ impl ProjectContainer {
             encryption_key,
             build_id,
             preview_props,
+            browserslist_query,
+            no_mangling,
         } = options;
 
         let resolved_self = self.to_resolved().await?;
@@ -388,6 +398,12 @@ impl ProjectContainer {
         if let Some(preview_props) = preview_props {
             new_options.preview_props = preview_props;
         }
+        if let Some(browserslist_query) = browserslist_query {
+            new_options.browserslist_query = browserslist_query;
+        }
+        if let Some(no_mangling) = no_mangling {
+            new_options.no_mangling = no_mangling;
+        }
 
         // TODO: Handle mode switch, should prevent mode being switched.
         let watch = new_options.watch;

filename: packages/next/src/build/swc/generated-native.d.ts
patch: @@ -123,8 +123,8 @@ export interface NapiProjectOptions {
    */
   projectPath: RcStr
   /**
-   * A path where to emit the build outputs, relative to [`Project::project_path`], always Unix
-   * path. Corresponds to next.config.js's `distDir`.
+   * A path where tracing output will be written to and/or cache is read/written.
+   * Usually equal to the `distDir` in next.config.js.
    * E.g. `.next`
    */
   distDir: RcStr
@@ -172,12 +172,6 @@ export interface NapiPartialProjectOptions {
    * E.g. `apps/my-app`
    */
   projectPath?: RcStr
-  /**
-   * A path where to emit the build outputs, relative to [`Project::project_path`], always a
-   * Unix path. Corresponds to next.config.js's `distDir`.
-   * E.g. `.next`
-   */
-  distDir?: RcStr | undefined | null
   /** Filesystem watcher options. */
   watch?: NapiWatchOptions
   /** The contents of next.config.js, serialized to JSON. */

filename: packages/next/src/build/swc/index.ts
patch: @@ -29,6 +29,7 @@ import type {
   Endpoint,
   HmrIdentifiers,
   Lockfile,
+  PartialProjectOptions,
   Project,
   ProjectOptions,
   RawEntrypoints,
@@ -651,15 +652,15 @@ function bindingToApi(
   }
 
   async function rustifyPartialProjectOptions(
-    options: Partial<ProjectOptions>
+    options: PartialProjectOptions
   ): Promise<NapiPartialProjectOptions> {
     return {
       ...options,
       nextConfig:
         options.nextConfig &&
         (await serializeNextConfig(
           options.nextConfig,
-          path.join(options.rootPath!, options.projectPath!)
+          path.join(options.rootPath, options.projectPath)
         )),
       env: options.env && rustifyEnv(options.env),
     }
@@ -672,7 +673,7 @@ function bindingToApi(
       this._nativeProject = nativeProject
     }
 
-    async update(options: Partial<ProjectOptions>) {
+    async update(options: PartialProjectOptions) {
       await binding.projectUpdate(
         this._nativeProject,
         await rustifyPartialProjectOptions(options)

filename: packages/next/src/build/swc/types.ts
patch: @@ -5,6 +5,8 @@ import type {
   RefCell,
   NapiTurboEngineOptions,
   NapiSourceDiagnostic,
+  NapiProjectOptions,
+  NapiPartialProjectOptions,
 } from './generated-native'
 
 export type { NapiTurboEngineOptions as TurboEngineOptions }
@@ -367,27 +369,8 @@ export type WrittenEndpoint =
       config: EndpointConfig
     }
 
-export interface ProjectOptions {
-  /**
-   * An absolute root path (Unix or Windows path) from which all files must be nested under. Trying
-   * to access a file outside this root will fail, so think of this as a chroot.
-   * E.g. `/home/user/projects/my-repo`.
-   */
-  rootPath: string
-
-  /**
-   * A path which contains the app/pages directories, relative to `root_path`, always a Unix path.
-   * E.g. `apps/my-app`
-   */
-  projectPath: string
-
-  /**
-   * A path where to emit the build outputs, relative to [`Project::project_path`], always a Unix
-   * path. Corresponds to next.config.js's `distDir`.
-   * E.g. `.next`
-   */
-  distDir: string
-
+export interface ProjectOptions
+  extends Omit<NapiProjectOptions, 'nextConfig' | 'env'> {
   /**
    * The next.config.js contents.
    */
@@ -397,53 +380,21 @@ export interface ProjectOptions {
    * A map of environment variables to use when compiling code.
    */
   env: Record<string, string>
+}
 
-  defineEnv: DefineEnv
-
-  /**
-   * Whether to watch the filesystem for file changes.
-   */
-  watch: {
-    enable: boolean
-    pollIntervalMs?: number
-  }
-
-  /**
-   * The mode in which Next.js is running.
-   */
-  dev: boolean
-
-  /**
-   * The server actions encryption key.
-   */
-  encryptionKey: string
-
-  /**
-   * The build id.
-   */
-  buildId: string
-
-  /**
-   * Options for draft mode.
-   */
-  previewProps: __ApiPreviewProps
-
-  /**
-   * The browserslist query to use for targeting browsers.
-   */
-  browserslistQuery: string
-
+export interface PartialProjectOptions
+  extends Omit<NapiPartialProjectOptions, 'nextConfig' | 'env'> {
+  rootPath: NapiProjectOptions['rootPath']
+  projectPath: NapiProjectOptions['projectPath']
   /**
-   * When the code is minified, this opts out of the default mangling of local
-   * names for variables, functions etc., which can be useful for
-   * debugging/profiling purposes.
+   * The next.config.js contents.
    */
-  noMangling: boolean
+  nextConfig?: NextConfigComplete
 
   /**
-   * The version of Node.js that is available/currently running.
+   * A map of environment variables to use when compiling code.
    */
-  currentNodeJsVersion: string
+  env?: Record<string, string>
 }
 
 export interface DefineEnv {