1use deno_core::op2;
9
10#[derive(Debug, thiserror::Error, deno_error::JsError)]
11#[class(generic)]
12pub enum CliParserError {
13 #[error(
14 "Failed to parse Node.js CLI arguments: {message}. If you believe this is a valid Node.js flag, please report it at https://github.com/denoland/deno/issues"
15 )]
16 ParseError { message: String },
17}
18
19pub use node_shim::DebugOptions;
20pub use node_shim::EnvironmentOptions;
21pub use node_shim::HostPort;
22pub use node_shim::InspectPublishUid;
23pub use node_shim::OptionEnvvarSettings;
24pub use node_shim::OptionType;
25pub use node_shim::OptionsParser;
26pub use node_shim::ParseResult;
27pub use node_shim::PerIsolateOptions;
28pub use node_shim::PerProcessOptions;
29pub use node_shim::TranslateOptions;
30pub use node_shim::TranslatedArgs as NodeShimTranslatedArgs;
31pub use node_shim::parse_args;
33pub use node_shim::parse_node_options_env_var;
34pub use node_shim::translate_to_deno_args as translate_to_deno_args_impl;
35pub use node_shim::wrap_eval_code;
36use serde::Serialize;
37
38#[derive(Debug, Clone, Serialize)]
40pub struct TranslatedArgs {
41 pub deno_args: Vec<String>,
43 pub node_options: Vec<String>,
45 pub needs_npm_process_state: bool,
47}
48
49fn translate_to_deno_args(
52 parsed_args: ParseResult,
53 script_in_npm_package: bool,
54 wrap_eval: bool,
55) -> TranslatedArgs {
56 let options = if wrap_eval {
57 TranslateOptions::for_child_process()
58 } else {
59 TranslateOptions::for_shell_command()
60 };
61 let result = translate_to_deno_args_impl(parsed_args, &options);
62
63 TranslatedArgs {
64 deno_args: result.deno_args,
65 node_options: result.node_options,
66 needs_npm_process_state: script_in_npm_package,
67 }
68}
69
70#[op2]
79#[serde]
80pub fn op_node_translate_cli_args(
81 #[serde] args: Vec<String>,
82 script_in_npm_package: bool,
83 wrap_eval: bool,
84) -> Result<TranslatedArgs, CliParserError> {
85 if args.is_empty() {
89 return Ok(TranslatedArgs {
90 deno_args: vec!["run".to_string(), "-A".to_string(), "-".to_string()],
91 node_options: vec![],
92 needs_npm_process_state: script_in_npm_package,
93 });
94 }
95
96 match parse_args(args.clone()) {
98 Ok(parsed) => Ok(translate_to_deno_args(
99 parsed,
100 script_in_npm_package,
101 wrap_eval,
102 )),
103 Err(unknown_flags) => Err(CliParserError::ParseError {
104 message: unknown_flags.join(", "),
105 }),
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 macro_rules! svec {
115 ($($x:expr),* $(,)?) => {
116 vec![$($x.to_string()),*]
117 };
118 }
119
120 #[test]
121 fn test_basic_parsing() {
122 let result = parse_args(svec!["--version"]).unwrap();
123 assert!(result.options.print_version);
124 }
125
126 #[test]
127 fn test_help_parsing() {
128 let result = parse_args(svec!["--help"]).unwrap();
129 assert!(result.options.print_help);
130 }
131
132 #[test]
133 fn test_debug_options() {
134 let result = parse_args(svec!["--inspect"]).unwrap();
135 assert!(
136 result
137 .options
138 .per_isolate
139 .per_env
140 .debug_options
141 .inspector_enabled
142 );
143 }
144
145 #[test]
146 fn test_string_option() {
147 let result = parse_args(svec!["--title", "myapp"]).unwrap();
148 assert_eq!(result.options.title, "myapp");
149 }
150
151 #[test]
152 fn test_boolean_negation() {
153 let result = parse_args(svec!["--no-warnings"]).unwrap();
154 assert!(!result.options.per_isolate.per_env.warnings);
155 }
156
157 #[test]
158 fn test_alias_expansion() {
159 let result = parse_args(svec!["-v"]).unwrap();
160 assert!(result.options.print_version);
161 }
162
163 #[test]
164 fn test_node_options_parsing() {
165 let env_args =
166 parse_node_options_env_var("--inspect --title \"my app\"").unwrap();
167 assert_eq!(env_args, vec!["--inspect", "--title", "my app"]);
168 }
169
170 #[test]
171 fn test_host_port_parsing() {
172 let result = parse_args(svec!["--inspect-port", "127.0.0.1:9229"]).unwrap();
173 assert_eq!(
174 result
175 .options
176 .per_isolate
177 .per_env
178 .debug_options
179 .host_port
180 .host,
181 "127.0.0.1"
182 );
183 assert_eq!(
184 result
185 .options
186 .per_isolate
187 .per_env
188 .debug_options
189 .host_port
190 .port,
191 9229
192 );
193 }
194
195 #[test]
196 fn test_translate_basic_script() {
197 let parsed = parse_args(svec!["script.js"]).unwrap();
198 let result = translate_to_deno_args(parsed, false, true);
199 assert_eq!(
200 result.deno_args,
201 svec![
202 "run",
203 "-A",
204 "--unstable-node-globals",
205 "--unstable-bare-node-builtins",
206 "--unstable-detect-cjs",
207 "script.js"
208 ]
209 );
210 assert!(result.node_options.is_empty());
211 assert!(!result.needs_npm_process_state);
212 }
213
214 #[test]
215 fn test_translate_version() {
216 let parsed = parse_args(svec!["--version"]).unwrap();
217 let result = translate_to_deno_args(parsed, false, true);
218 assert_eq!(result.deno_args, svec!["--version"]);
219 }
220
221 #[test]
222 fn test_translate_help() {
223 let parsed = parse_args(svec!["--help"]).unwrap();
224 let result = translate_to_deno_args(parsed, false, true);
225 assert_eq!(result.deno_args, svec!["--help"]);
226 }
227
228 #[test]
229 fn test_translate_eval() {
230 let parsed = parse_args(svec!["--eval", "console.log(42)"]).unwrap();
231 let result = translate_to_deno_args(parsed, false, true);
232 assert!(result.deno_args.contains(&"eval".to_string()));
234 assert!(result.deno_args.iter().any(|a| {
237 a.contains(r#"process.getBuiltinModule("vm").runInThisContext"#)
238 }));
239 }
240
241 #[test]
242 fn test_translate_inspect() {
243 let parsed = parse_args(svec!["--inspect", "script.js"]).unwrap();
244 let result = translate_to_deno_args(parsed, false, true);
245 assert!(
246 result
247 .deno_args
248 .contains(&"--inspect=127.0.0.1:9229".to_string())
249 );
250 assert!(result.deno_args.contains(&"script.js".to_string()));
251 }
252
253 #[test]
254 fn test_translate_inspect_brk() {
255 let parsed = parse_args(svec!["--inspect-brk", "script.js"]).unwrap();
256 let result = translate_to_deno_args(parsed, false, true);
257 assert!(
258 result
259 .deno_args
260 .contains(&"--inspect-brk=127.0.0.1:9229".to_string())
261 );
262 }
263
264 #[test]
265 fn test_translate_watch() {
266 let parsed = parse_args(svec!["--watch", "script.js"]).unwrap();
267 let result = translate_to_deno_args(parsed, false, true);
268 assert!(result.deno_args.contains(&"--watch".to_string()));
269 }
270
271 #[test]
272 fn test_translate_no_warnings() {
273 let parsed = parse_args(svec!["--no-warnings", "script.js"]).unwrap();
274 let result = translate_to_deno_args(parsed, false, true);
275 assert!(result.deno_args.contains(&"--quiet".to_string()));
276 assert!(result.node_options.contains(&"--no-warnings".to_string()));
277 }
278
279 #[test]
280 fn test_translate_conditions() {
281 let parsed =
282 parse_args(svec!["--conditions", "development", "script.js"]).unwrap();
283 let result = translate_to_deno_args(parsed, false, true);
284 assert!(
285 result
286 .deno_args
287 .contains(&"--conditions=development".to_string())
288 );
289 }
290
291 #[test]
292 fn test_translate_conditions_equals_format() {
293 let parsed = parse_args(svec!["--conditions=custom", "script.js"]).unwrap();
295 let result = translate_to_deno_args(parsed, false, true);
296 assert!(
297 result
298 .deno_args
299 .contains(&"--conditions=custom".to_string()),
300 );
301 }
302
303 #[test]
304 fn test_translate_conditions_short_alias() {
305 let parsed = parse_args(svec!["-C", "custom", "script.js"]).unwrap();
307 let result = translate_to_deno_args(parsed, false, true);
308 assert!(
309 result
310 .deno_args
311 .contains(&"--conditions=custom".to_string()),
312 );
313 }
314
315 #[test]
316 fn test_translate_v8_flags() {
317 let parsed =
318 parse_args(svec!["--max-old-space-size=4096", "script.js"]).unwrap();
319 let result = translate_to_deno_args(parsed, false, true);
320 assert!(result.deno_args.iter().any(|a| a.contains("--v8-flags=")));
321 }
322
323 #[test]
324 fn test_translate_repl() {
325 let parsed = parse_args(svec![]).unwrap();
326 let result = translate_to_deno_args(parsed, false, true);
327 assert!(result.deno_args.is_empty());
329 }
330
331 #[test]
332 fn test_translate_npm_package() {
333 let parsed = parse_args(svec!["script.js"]).unwrap();
334 let result = translate_to_deno_args(parsed, true, true);
335 assert!(result.needs_npm_process_state);
336 }
337
338 #[test]
339 fn test_translate_run_script() {
340 let parsed = parse_args(svec!["--run", "build"]).unwrap();
341 let result = translate_to_deno_args(parsed, false, true);
342 assert_eq!(result.deno_args, svec!["task", "build"]);
343 }
344
345 #[test]
346 fn test_translate_test_runner() {
347 let parsed = parse_args(svec!["--test", "test.js"]).unwrap();
348 let result = translate_to_deno_args(parsed, false, true);
349 assert!(result.deno_args.contains(&"test".to_string()));
350 assert!(result.deno_args.contains(&"-A".to_string()));
351 assert!(result.deno_args.contains(&"test.js".to_string()));
352 }
353
354 #[test]
355 fn test_translate_test_with_watch() {
356 let parsed = parse_args(svec!["--test", "--watch", "test.js"]).unwrap();
357 let result = translate_to_deno_args(parsed, false, true);
358 assert!(result.deno_args.contains(&"test".to_string()));
359 assert!(result.deno_args.contains(&"--watch".to_string()));
360 }
361
362 #[test]
363 fn test_wrap_eval_code() {
364 let wrapped = wrap_eval_code("console.log(42)");
365 assert!(
366 wrapped.contains(r#"process.getBuiltinModule("vm").runInThisContext"#)
367 );
368 assert!(wrapped.contains("process.getBuiltinModule"));
369 assert!(wrapped.contains("\"console.log(42)\""));
370 }
371}