1use std::path::PathBuf;
2
3use anyhow::Context;
4use napi::bindgen_prelude::*;
5use next_build::{
6 BuildOptions as NextBuildOptions,
7 build_options::{BuildContext, DefineEnv},
8};
9use next_core::next_config::{Rewrite, Rewrites, RouteHas};
10
11use crate::next_api::project::NapiDefineEnv;
12
13#[napi(object, object_to_js = false)]
14#[derive(Debug)]
15pub struct NextBuildContext {
16 pub root: Option<String>,
19
20 pub dir: Option<String>,
22
23 pub dist_dir: Option<String>,
27
28 pub build_id: Option<String>,
30
31 pub rewrites: Option<NapiRewrites>,
33 pub define_env: NapiDefineEnv,
40}
41
42impl TryFrom<NextBuildContext> for NextBuildOptions {
43 type Error = napi::Error;
44
45 fn try_from(value: NextBuildContext) -> Result<Self> {
46 Ok(Self {
47 dir: value.dir.map(PathBuf::try_from).transpose()?,
48 root: value.root.map(PathBuf::try_from).transpose()?,
49 log_level: None,
50 show_all: true,
51 log_detail: true,
52 full_stats: true,
53 memory_limit: None,
54 dist_dir: value.dist_dir,
55 build_context: Some(BuildContext {
56 build_id: value
57 .build_id
58 .context("NextBuildContext must provide a build ID")?,
59 rewrites: value
60 .rewrites
61 .context("NextBuildContext must provide rewrites")?
62 .into(),
63 }),
64 define_env: value.define_env.into(),
65 })
66 }
67}
68
69impl From<NapiDefineEnv> for DefineEnv {
70 fn from(val: NapiDefineEnv) -> Self {
71 DefineEnv {
72 client: val
73 .client
74 .into_iter()
75 .map(|var| (var.name, var.value))
76 .collect(),
77 edge: val
78 .edge
79 .into_iter()
80 .map(|var| (var.name, var.value))
81 .collect(),
82 nodejs: val
83 .nodejs
84 .into_iter()
85 .map(|var| (var.name, var.value))
86 .collect(),
87 }
88 }
89}
90
91#[napi(object, object_to_js = false)]
93#[derive(Debug)]
94pub struct NapiRewrites {
95 pub fallback: Vec<NapiRewrite>,
96 pub after_files: Vec<NapiRewrite>,
97 pub before_files: Vec<NapiRewrite>,
98}
99
100impl From<NapiRewrites> for Rewrites {
101 fn from(val: NapiRewrites) -> Self {
102 Rewrites {
103 fallback: val
104 .fallback
105 .into_iter()
106 .map(|rewrite| rewrite.into())
107 .collect(),
108 after_files: val
109 .after_files
110 .into_iter()
111 .map(|rewrite| rewrite.into())
112 .collect(),
113 before_files: val
114 .before_files
115 .into_iter()
116 .map(|rewrite| rewrite.into())
117 .collect(),
118 }
119 }
120}
121
122#[napi(object, object_to_js = false)]
124#[derive(Debug)]
125pub struct NapiRewrite {
126 pub source: String,
127 pub destination: String,
128 pub base_path: Option<bool>,
129 pub locale: Option<bool>,
130 pub has: Option<Vec<NapiRouteHas>>,
131 pub missing: Option<Vec<NapiRouteHas>>,
132}
133
134impl From<NapiRewrite> for Rewrite {
135 fn from(val: NapiRewrite) -> Self {
136 Rewrite {
137 source: val.source,
138 destination: val.destination,
139 base_path: val.base_path,
140 locale: val.locale,
141 has: val
142 .has
143 .map(|has| has.into_iter().map(|has| has.into()).collect()),
144 missing: val
145 .missing
146 .map(|missing| missing.into_iter().map(|missing| missing.into()).collect()),
147 }
148 }
149}
150
151#[derive(Debug)]
153pub enum NapiRouteHas {
154 Header { key: String, value: Option<String> },
155 Query { key: String, value: Option<String> },
156 Cookie { key: String, value: Option<String> },
157 Host { value: String },
158}
159
160impl FromNapiValue for NapiRouteHas {
161 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
162 let object = unsafe { Object::from_napi_value(env, napi_val)? };
163 let type_ = object.get_named_property::<String>("type")?;
164 Ok(match type_.as_str() {
165 "header" => NapiRouteHas::Header {
166 key: object.get_named_property("key")?,
167 value: object.get_named_property("value")?,
168 },
169 "query" => NapiRouteHas::Query {
170 key: object.get_named_property("key")?,
171 value: object.get_named_property("value")?,
172 },
173 "cookie" => NapiRouteHas::Cookie {
174 key: object.get_named_property("key")?,
175 value: object.get_named_property("value")?,
176 },
177 "host" => NapiRouteHas::Host {
178 value: object.get_named_property("value")?,
179 },
180 _ => {
181 return Err(napi::Error::new(
182 Status::GenericFailure,
183 format!("invalid type for RouteHas: {type_}"),
184 ));
185 }
186 })
187 }
188}
189
190impl From<NapiRouteHas> for RouteHas {
191 fn from(val: NapiRouteHas) -> Self {
192 match val {
193 NapiRouteHas::Header { key, value } => RouteHas::Header {
194 key: key.into(),
195 value: value.map(From::from),
196 },
197 NapiRouteHas::Query { key, value } => RouteHas::Query {
198 key: key.into(),
199 value: value.map(From::from),
200 },
201 NapiRouteHas::Cookie { key, value } => RouteHas::Cookie {
202 key: key.into(),
203 value: value.map(From::from),
204 },
205 NapiRouteHas::Host { value } => RouteHas::Host {
206 value: value.into(),
207 },
208 }
209 }
210}