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