Skip to content

Commit 7add659

Browse files
authored
feat(node): support packages with multiple executables (#1226)
* feat(runner): add support for npm package executable names * feat: finish implementing executable names * fix: working support for deno * refactor: temporarily disable yarn tests * refactor(codegen): support disabling homebrew install
1 parent cf9cb33 commit 7add659

File tree

180 files changed

+1055
-518
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

180 files changed

+1055
-518
lines changed

.github/workflows/test.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ jobs:
8989
- run: curl -LsSf https://github.com/posit-dev/air/releases/latest/download/air-installer.sh | sh
9090
- name: actionlint
9191
run: ( ( which actionlint ) || ( ( which brew && brew install actionlint ) || ( which go && go install github.com/rhysd/actionlint/cmd/actionlint@latest ) ) || ( echo "Unable to install tool" ) )
92+
- name: air
93+
run: ( ( which air ) || ( ( which brew && brew install air ) ) || ( echo "Unable to install tool" ) )
9294
- name: alejandra
9395
run: ( ( which alejandra ) || ( ( which cargo && ( cargo binstall alejandra || cargo install alejandra ) ) ) || ( echo "Unable to install tool" ) )
9496
- name: alex
@@ -131,6 +133,8 @@ jobs:
131133
run: ( ( which csscomb ) || ( ( which npm && npm i -g csscomb ) ) || ( echo "Unable to install tool" ) )
132134
- name: csslint
133135
run: ( ( which csslint ) || ( ( which npm && npm i -g csslint ) ) || ( echo "Unable to install tool" ) )
136+
- name: deno
137+
run: ( ( which deno ) || ( ( which cargo && ( cargo binstall deno || cargo install deno ) ) || ( which brew && brew install deno ) ) || ( echo "Unable to install tool" ) )
134138
- name: dfmt
135139
run: ( ( which dfmt ) || ( ( which brew && brew install dfmt ) ) || ( echo "Unable to install tool" ) )
136140
- name: dockerfmt
@@ -331,6 +335,8 @@ jobs:
331335
run: ( ( which toml-sort ) || ( ( which pipx && pipx install toml-sort ) ) || ( echo "Unable to install tool" ) )
332336
- name: topiary
333337
run: ( ( which topiary ) || ( ( which cargo && ( cargo binstall topiary-cli || cargo install topiary-cli ) ) ) || ( echo "Unable to install tool" ) )
338+
- name: tsp
339+
run: ( ( which tsp ) || ( ( which npm && npm i -g @typespec/compiler ) ) || ( echo "Unable to install tool" ) )
334340
- name: ty
335341
run: ( ( which ty ) || ( ( which pipx && pipx install ty ) ) || ( echo "Unable to install tool" ) )
336342
- name: typos

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. Dates are d
44

55
#### [Unreleased](https://github.com/hougesen/mdsf/compare/v0.10.4...HEAD)
66

7+
- feat(node): support packages with multiple executables [`#1226`](https://github.com/hougesen/mdsf/pull/1226)
8+
- build(deps-dev): bump typescript from 5.8.3 to 5.9.2 in /mdsf-vscode [`#1221`](https://github.com/hougesen/mdsf/pull/1221)
9+
- build(deps-dev): bump @typescript-eslint/eslint-plugin in /mdsf-vscode [`#1223`](https://github.com/hougesen/mdsf/pull/1223)
10+
- build(deps-dev): bump @biomejs/biome in /github-action [`#1224`](https://github.com/hougesen/mdsf/pull/1224)
11+
- build(deps-dev): bump @biomejs/biome from 2.1.2 to 2.1.3 in /mdsf-vscode [`#1225`](https://github.com/hougesen/mdsf/pull/1225)
12+
- build(deps-dev): bump @typescript-eslint/parser in /mdsf-vscode [`#1222`](https://github.com/hougesen/mdsf/pull/1222)
13+
- build(cargo-dist): bump version to v0.29.0 [`#1220`](https://github.com/hougesen/mdsf/pull/1220)
714
- build(deps): bump serde_json from 1.0.141 to 1.0.142 [`#1219`](https://github.com/hougesen/mdsf/pull/1219)
815
- build(deps): bump clap from 4.5.41 to 4.5.42 [`#1218`](https://github.com/hougesen/mdsf/pull/1218)
916

codegen/src/actions/packages.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ fn generate_brew(tool: &ToolPackagesBrew) -> String {
4040
.map(|tap| format!("brew tap {tap} &&"))
4141
.unwrap_or_default();
4242

43-
format!("( which brew && {} brew install {} )", tap, tool.package)
43+
let package_name = &tool.package;
44+
45+
let cask = if tool.cask { "--cask " } else { "" };
46+
47+
format!("( which brew && {tap} brew install {cask}{package_name} )")
4448
}
4549

4650
fn generate_luarocks(tool: &str) -> String {
@@ -106,7 +110,9 @@ pub fn generate_install_steps(tools: &Vec<Tool>) -> Vec<WorkflowJobsStep> {
106110
}
107111

108112
if let Some(brew) = &tool.packages.brew {
109-
install_options.push(generate_brew(brew));
113+
if !brew.skip_brew_install {
114+
install_options.push(generate_brew(brew));
115+
}
110116
}
111117

112118
if let Some(apt) = &tool.packages.apt {

codegen/src/tools.rs

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ pub struct ToolPackagesApt {
5858
#[derive(Debug, serde::Deserialize, schemars::JsonSchema, Clone, Default, serde::Serialize)]
5959
#[serde(deny_unknown_fields)]
6060
pub struct ToolPackagesBrew {
61+
#[serde(default, skip_serializing_if = "is_false")]
62+
pub cask: bool,
63+
64+
#[serde(default, skip_serializing_if = "is_false")]
65+
pub skip_brew_install: bool,
66+
6167
pub package: String,
6268

6369
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -138,7 +144,26 @@ pub struct ToolPackagesNimble {
138144

139145
#[derive(Debug, serde::Deserialize, schemars::JsonSchema, Clone, Default, serde::Serialize)]
140146
#[serde(deny_unknown_fields)]
147+
#[allow(clippy::struct_excessive_bools)]
141148
pub struct ToolPackagesNpm {
149+
#[serde(default, skip_serializing_if = "is_false")]
150+
pub disable_bunx: bool,
151+
152+
#[serde(default, skip_serializing_if = "is_false")]
153+
pub disable_deno_run: bool,
154+
155+
#[serde(default, skip_serializing_if = "is_false")]
156+
pub disable_npx: bool,
157+
158+
#[serde(default, skip_serializing_if = "is_false")]
159+
pub disable_pnpm_dlx: bool,
160+
161+
#[serde(default, skip_serializing_if = "is_false")]
162+
pub disable_yarn_exec: bool,
163+
164+
#[serde(default, skip_serializing_if = "Option::is_none")]
165+
pub executable: Option<String>,
166+
142167
pub package: String,
143168
}
144169

@@ -388,6 +413,7 @@ impl Tool {
388413
}
389414

390415
#[allow(clippy::too_many_lines)]
416+
#[allow(clippy::cognitive_complexity)]
391417
fn generate(&self) -> Vec<GeneratedCommand> {
392418
let mut all_commands = Vec::new();
393419

@@ -411,28 +437,56 @@ impl Tool {
411437
command_types.push(format!("CommandType::Direct(\"{}\")", self.binary));
412438

413439
if let Some(npm) = &self.packages.npm {
414-
command_types.push(format!("CommandType::Npm(\"{}\")", &npm.package));
440+
let package_name = &npm.package;
441+
let executable_name = npm.executable.as_ref().unwrap_or(&npm.package);
442+
443+
let mut flavors = Vec::new();
415444

416-
command_types.push(format!("CommandType::Pnpm(\"{}\")", &npm.package));
445+
if !npm.disable_npx {
446+
flavors.push("Npm");
447+
}
417448

418-
command_types.push(format!("CommandType::Bun(\"{}\")", &npm.package));
449+
if !npm.disable_pnpm_dlx {
450+
flavors.push("Pnpm");
451+
}
419452

420-
command_types.push(format!("CommandType::Deno(\"{}\")", &npm.package));
453+
if !npm.disable_bunx {
454+
flavors.push("Bun");
455+
}
456+
457+
if !npm.disable_deno_run {
458+
flavors.push("Deno");
459+
}
421460

422-
command_types.push(format!("CommandType::Yarn(\"{}\")", &npm.package));
461+
if !npm.disable_yarn_exec {
462+
flavors.push("Yarn");
463+
}
464+
465+
for flavor in flavors {
466+
command_types.push(format!(
467+
"CommandType::{flavor}(\"{package_name}\", \"{executable_name}\")"
468+
));
469+
}
423470
}
424471

425472
if let Some(pip) = &self.packages.pip {
473+
let package_name = &pip.package;
474+
let executable_name = pip.executable.as_ref().unwrap_or(&pip.package);
475+
476+
let mut flavors = Vec::new();
477+
426478
if !pip.disable_uv_tool_run {
427-
command_types.push(format!(
428-
"CommandType::Uv(\"{}\", \"{}\")",
429-
&pip.package,
430-
&pip.executable.clone().as_ref().unwrap_or(&pip.package)
431-
));
479+
flavors.push("Uv");
432480
}
433481

434482
if !pip.disable_pipx_run {
435-
command_types.push(format!("CommandType::Pipx(\"{}\")", &pip.package));
483+
flavors.push("Pipx");
484+
}
485+
486+
for flavor in flavors {
487+
command_types.push(format!(
488+
"CommandType::{flavor}(\"{package_name}\", \"{executable_name}\")"
489+
));
436490
}
437491
}
438492

github-action/biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "https://biomejs.dev/schemas/2.1.2/schema.json",
2+
"$schema": "https://biomejs.dev/schemas/2.1.3/schema.json",
33
"assist": {
44
"actions": {
55
"source": {

mdsf-vscode/biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "https://biomejs.dev/schemas/2.1.2/schema.json",
2+
"$schema": "https://biomejs.dev/schemas/2.1.3/schema.json",
33
"assist": {
44
"actions": {
55
"source": {

mdsf/src/execution/mod.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,10 @@ mod test_run_tools {
423423
#[test]
424424
fn it_should_skip_if_bun_runner_is_disabled() -> Result<(), MdsfError> {
425425
let (was_not_modified, _) = super::run_tools(
426-
&[CommandType::Bun("thisbinarydoesnotexist")],
426+
&[CommandType::Bun(
427+
"thisbinarydoesnotexist",
428+
"thisbinarydoesnotexist",
429+
)],
427430
std::path::Path::new("thisdoesnotexist"),
428431
|_, _| unreachable!(),
429432
crate::testing::DEFAULT_TEST_FORMATTER_TIMEOUT,
@@ -452,7 +455,10 @@ mod test_run_tools {
452455
#[test]
453456
fn it_should_skip_if_deno_runner_is_disabled() -> Result<(), MdsfError> {
454457
let (was_not_modified, _) = super::run_tools(
455-
&[CommandType::Deno("thisbinarydoesnotexist")],
458+
&[CommandType::Deno(
459+
"thisbinarydoesnotexist",
460+
"thisbinarydoesnotexist",
461+
)],
456462
std::path::Path::new("thisdoesnotexist"),
457463
|_, _| unreachable!(),
458464
crate::testing::DEFAULT_TEST_FORMATTER_TIMEOUT,
@@ -572,7 +578,10 @@ mod test_run_tools {
572578
#[test]
573579
fn it_should_skip_if_npx_runner_is_disabled() -> Result<(), MdsfError> {
574580
let (was_not_modified, _) = super::run_tools(
575-
&[CommandType::Npm("thisbinarydoesnotexist")],
581+
&[CommandType::Npm(
582+
"thisbinarydoesnotexist",
583+
"thisbinarydoesnotexist",
584+
)],
576585
std::path::Path::new("thisdoesnotexist"),
577586
|_, _| unreachable!(),
578587
crate::testing::DEFAULT_TEST_FORMATTER_TIMEOUT,
@@ -602,7 +611,10 @@ mod test_run_tools {
602611
#[test]
603612
fn it_should_skip_if_pipx_runner_is_disabled() -> Result<(), MdsfError> {
604613
let (was_not_modified, _) = super::run_tools(
605-
&[CommandType::Pipx("thisbinarydoesnotexist")],
614+
&[CommandType::Pipx(
615+
"thisbinarydoesnotexist",
616+
"thisbinarydoesnotexist",
617+
)],
606618
std::path::Path::new("thisdoesnotexist"),
607619
|_, _| unreachable!(),
608620
crate::testing::DEFAULT_TEST_FORMATTER_TIMEOUT,
@@ -632,7 +644,10 @@ mod test_run_tools {
632644
#[test]
633645
fn it_should_skip_if_pnpm_runner_is_disabled() -> Result<(), MdsfError> {
634646
let (was_not_modified, _) = super::run_tools(
635-
&[CommandType::Pnpm("thisbinarydoesnotexist")],
647+
&[CommandType::Pnpm(
648+
"thisbinarydoesnotexist",
649+
"thisbinarydoesnotexist",
650+
)],
636651
std::path::Path::new("thisdoesnotexist"),
637652
|_, _| unreachable!(),
638653
crate::testing::DEFAULT_TEST_FORMATTER_TIMEOUT,
@@ -695,7 +710,10 @@ mod test_run_tools {
695710
#[test]
696711
fn it_should_skip_if_yarn_runner_is_disabled() -> Result<(), MdsfError> {
697712
let (was_not_modified, _) = super::run_tools(
698-
&[CommandType::Yarn("thisbinarydoesnotexist")],
713+
&[CommandType::Yarn(
714+
"thisbinarydoesnotexist",
715+
"thisbinarydoesnotexist",
716+
)],
699717
std::path::Path::new("thisdoesnotexist"),
700718
|_, _| unreachable!(),
701719
crate::testing::DEFAULT_TEST_FORMATTER_TIMEOUT,

mdsf/src/runners/bun.rs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#[inline]
2-
pub fn setup_bunx_command(package_name: &str) -> std::process::Command {
2+
pub fn setup_bunx_command(package_name: &str, _executable_name: &str) -> std::process::Command {
33
let mut cmd = std::process::Command::new("bunx");
44

55
// Auto install package
66
cmd.arg("--yes");
77

8+
// TODO: figure out how to actually select executable using bun
89
cmd.arg(package_name);
910

1011
cmd
@@ -15,15 +16,16 @@ mod test_bun {
1516
#[test_with::executable(bunx)]
1617
#[test]
1718
fn it_can_execute_an_npm_package_script() {
18-
let input = "";
19+
let input = "[1,2,3,4,5,6]";
20+
let output = "[1, 2, 3, 4, 5, 6]\n";
1921

2022
let file_ext = crate::filetype::get_file_extension("json");
2123

2224
let snippet =
2325
crate::execution::setup_snippet(input, &file_ext).expect("it to create a snippet file");
2426

25-
crate::execution::run_tools(
26-
&[crate::runners::CommandType::Bun("prettier")],
27+
let result = crate::execution::run_tools(
28+
&[crate::runners::CommandType::Bun("prettier", "prettier")],
2729
snippet.path(),
2830
crate::tools::prettier::set_args,
2931
crate::testing::DEFAULT_TEST_FORMATTER_TIMEOUT,
@@ -34,6 +36,50 @@ mod test_bun {
3436
..Default::default()
3537
},
3638
)
37-
.expect("it to succeed");
39+
.expect("it to succeed")
40+
.1
41+
.expect("it to be some");
42+
43+
assert_eq!(result, output);
44+
}
45+
46+
#[test_with::executable(bunx)]
47+
#[test]
48+
fn it_works_with_executable_name() {
49+
let input = r#"model Pet { name: string; age: int32;kind: "dog" | "cat" | "fish";}
50+
"#;
51+
52+
let output = r#"model Pet {
53+
name: string;
54+
age: int32;
55+
kind: "dog" | "cat" | "fish";
56+
}
57+
"#;
58+
59+
let file_ext = crate::filetype::get_file_extension("typespec");
60+
61+
let snippet =
62+
crate::execution::setup_snippet(input, &file_ext).expect("it to create a snippet file");
63+
64+
let result = crate::execution::run_tools(
65+
&[crate::runners::CommandType::Bun(
66+
"@typespec/compiler",
67+
"tsp",
68+
)],
69+
snippet.path(),
70+
crate::tools::tsp_format::set_args,
71+
crate::testing::DEFAULT_TEST_FORMATTER_TIMEOUT,
72+
crate::tools::tsp_format::IS_STDIN,
73+
crate::testing::DEFAULT_TEST_DEBUG_ENABLED,
74+
&crate::config::MdsfConfigRunners {
75+
bunx: true,
76+
..Default::default()
77+
},
78+
)
79+
.expect("it to be successful")
80+
.1
81+
.expect("it to be some");
82+
83+
assert_eq!(result, output);
3884
}
3985
}

mdsf/src/runners/composer.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub fn setup_php_vender_bin_command(binary_name: &str) -> std::process::Command
66
}
77

88
#[cfg(test)]
9-
mod test_mago_format {
9+
mod test_composer {
1010
#[test_with::executable(./vendor/bin/mago)]
1111
fn test_composer_path() {
1212
let input = r#"<?php
@@ -28,7 +28,18 @@ echo 'Hello World!';
2828
crate::testing::DEFAULT_TEST_FORMATTER_TIMEOUT,
2929
crate::tools::mago_format::IS_STDIN,
3030
crate::testing::DEFAULT_TEST_DEBUG_ENABLED,
31-
&crate::config::MdsfConfigRunners::default(),
31+
&crate::config::MdsfConfigRunners {
32+
bunx: false,
33+
deno: false,
34+
dotnet: false,
35+
dub: false,
36+
gem_exec: false,
37+
npx: false,
38+
pipx: false,
39+
pnpm: false,
40+
uv: false,
41+
yarn: false,
42+
},
3243
)
3344
.expect("it to be successful")
3445
.1

0 commit comments

Comments
 (0)