electron-cli 0.2.8 → 0.3.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +266 -0
- package/Cargo.toml +14 -0
- package/LICENSE +15 -14
- package/README.md +60 -51
- package/bin/electron-cli.js +50 -0
- package/package.json +37 -79
- package/rust-toolchain.toml +3 -0
- package/scripts/install.js +101 -0
- package/src/cli.rs +36 -0
- package/src/commands/doctor.rs +355 -0
- package/src/commands/inspect.rs +63 -0
- package/src/commands/mod.rs +3 -0
- package/src/commands/plan.rs +235 -0
- package/src/main.rs +25 -0
- package/src/output.rs +7 -0
- package/src/project.rs +320 -0
- package/tests/fixtures/electron-forge/package-lock.json +23 -0
- package/tests/fixtures/electron-forge/package.json +21 -0
- package/tests/fixtures/electron-forge/src/main.ts +8 -0
- package/.babelrc +0 -14
- package/.eslintrc +0 -7
- package/.npmignore +0 -49
- package/dist/cli.js +0 -15
- package/dist/commands/init.js +0 -286
- package/dist/commands/pack.js +0 -78
- package/dist/commands/start.js +0 -76
- package/dist/commands/stats.js +0 -79
- package/dist/init/dir.js +0 -29
- package/dist/init/git.js +0 -25
- package/dist/init/json.js +0 -31
- package/dist/init/npm.js +0 -17
- package/dist/util/change-dir.js +0 -22
- package/dist/util/get-versions.js +0 -53
- package/dist/util/pack.js +0 -17
- package/dist/util/path-from-cwd.js +0 -13
- package/dist/util/start-electron.js +0 -51
- package/dist/util/terminate.js +0 -25
- package/dist/util/version.js +0 -10
- package/dist/validate/check-system.js +0 -56
- package/dist/validate/name.js +0 -34
- package/gulpfile.js +0 -94
- package/yarn.lock +0 -4118
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
use std::collections::BTreeMap;
|
|
2
|
+
|
|
3
|
+
use anyhow::Result;
|
|
4
|
+
use serde::Serialize;
|
|
5
|
+
|
|
6
|
+
use crate::{cli::CommandArgs, output, project};
|
|
7
|
+
|
|
8
|
+
#[derive(Debug, Serialize)]
|
|
9
|
+
struct PlanReport {
|
|
10
|
+
project_type: ProjectType,
|
|
11
|
+
recommended_commands: BTreeMap<String, String>,
|
|
12
|
+
missing: Vec<String>,
|
|
13
|
+
risks: Vec<String>,
|
|
14
|
+
notes: Vec<String>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#[derive(Debug, Serialize)]
|
|
18
|
+
#[serde(rename_all = "kebab-case")]
|
|
19
|
+
enum ProjectType {
|
|
20
|
+
ElectronForge,
|
|
21
|
+
Electron,
|
|
22
|
+
JavaScript,
|
|
23
|
+
Unknown,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn run(args: CommandArgs) -> Result<()> {
|
|
27
|
+
let snapshot = project::inspect(&args.cwd)?;
|
|
28
|
+
let report = build_report(&snapshot);
|
|
29
|
+
|
|
30
|
+
if args.json {
|
|
31
|
+
output::json(&report)
|
|
32
|
+
} else {
|
|
33
|
+
println!("electron-cli plan");
|
|
34
|
+
println!();
|
|
35
|
+
println!("Project");
|
|
36
|
+
println!(" root: {}", snapshot.root);
|
|
37
|
+
match snapshot.package_label() {
|
|
38
|
+
Some(label) => println!(" package: {label}"),
|
|
39
|
+
None => println!(" package: not found"),
|
|
40
|
+
}
|
|
41
|
+
println!(" type: {}", report.project_type.as_str());
|
|
42
|
+
|
|
43
|
+
if !report.recommended_commands.is_empty() {
|
|
44
|
+
println!();
|
|
45
|
+
println!("Recommended Commands");
|
|
46
|
+
for (name, command) in &report.recommended_commands {
|
|
47
|
+
println!(" {name}: {command}");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if !report.missing.is_empty() {
|
|
52
|
+
println!();
|
|
53
|
+
println!("Missing");
|
|
54
|
+
for item in &report.missing {
|
|
55
|
+
println!(" {item}");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if !report.risks.is_empty() {
|
|
60
|
+
println!();
|
|
61
|
+
println!("Risks");
|
|
62
|
+
for risk in &report.risks {
|
|
63
|
+
println!(" {risk}");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if !report.notes.is_empty() {
|
|
68
|
+
println!();
|
|
69
|
+
println!("Notes");
|
|
70
|
+
for note in &report.notes {
|
|
71
|
+
println!(" {note}");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Ok(())
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fn build_report(snapshot: &project::ProjectSnapshot) -> PlanReport {
|
|
80
|
+
let project_type = detect_project_type(snapshot);
|
|
81
|
+
let mut recommended_commands = BTreeMap::new();
|
|
82
|
+
let mut missing = Vec::new();
|
|
83
|
+
let mut risks = Vec::new();
|
|
84
|
+
let mut notes = Vec::new();
|
|
85
|
+
|
|
86
|
+
if let Some(script) = first_script(snapshot, &["start", "dev"]) {
|
|
87
|
+
recommended_commands.insert("dev".to_string(), run_script(snapshot, script));
|
|
88
|
+
} else if snapshot.electron_dependency.is_some() && snapshot.main.is_some() {
|
|
89
|
+
recommended_commands.insert("dev".to_string(), package_exec(snapshot, "electron ."));
|
|
90
|
+
missing.push(
|
|
91
|
+
"Add a start or dev script so humans and agents have a stable entrypoint.".to_string(),
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
missing.push("No start or dev script was found.".to_string());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if let Some(script) = first_script(snapshot, &["package", "pack"]) {
|
|
98
|
+
recommended_commands.insert("package".to_string(), run_script(snapshot, script));
|
|
99
|
+
} else if matches!(project_type, ProjectType::ElectronForge) {
|
|
100
|
+
missing.push(
|
|
101
|
+
"No package script was found even though Electron Forge is declared.".to_string(),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if let Some(script) = first_script(snapshot, &["make", "dist"]) {
|
|
106
|
+
recommended_commands.insert("make".to_string(), run_script(snapshot, script));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
recommended_commands.insert(
|
|
110
|
+
"diagnostics".to_string(),
|
|
111
|
+
"electron-cli doctor --json".to_string(),
|
|
112
|
+
);
|
|
113
|
+
recommended_commands.insert(
|
|
114
|
+
"inspect".to_string(),
|
|
115
|
+
"electron-cli inspect --json".to_string(),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if snapshot.package_json.is_none() {
|
|
119
|
+
risks.push(
|
|
120
|
+
"No package.json was found, so Electron project detection is limited.".to_string(),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if snapshot.electron_dependency.is_none() {
|
|
125
|
+
risks.push("Electron is not declared in package dependencies.".to_string());
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if snapshot.electron_dependency.is_some() && snapshot.main.is_none() {
|
|
129
|
+
risks.push("Electron is declared, but package.json has no main process entry.".to_string());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if snapshot.has_javascript_dependencies() && snapshot.package_manager.is_none() {
|
|
133
|
+
risks.push("JavaScript dependencies are declared, but no lockfile was found.".to_string());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if matches!(project_type, ProjectType::ElectronForge) {
|
|
137
|
+
notes.push("Electron Forge was detected; wrapping Forge commands is the safest workflow path today.".to_string());
|
|
138
|
+
} else if snapshot.electron_dependency.is_some() {
|
|
139
|
+
notes.push("Electron was detected without Forge; prefer inspection before choosing a packaging path.".to_string());
|
|
140
|
+
} else {
|
|
141
|
+
notes.push("This does not currently look like an Electron app.".to_string());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
PlanReport {
|
|
145
|
+
project_type,
|
|
146
|
+
recommended_commands,
|
|
147
|
+
missing,
|
|
148
|
+
risks,
|
|
149
|
+
notes,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fn detect_project_type(snapshot: &project::ProjectSnapshot) -> ProjectType {
|
|
154
|
+
if !snapshot.forge_dependencies.is_empty()
|
|
155
|
+
|| snapshot
|
|
156
|
+
.scripts
|
|
157
|
+
.values()
|
|
158
|
+
.any(|script| script.contains("electron-forge"))
|
|
159
|
+
{
|
|
160
|
+
ProjectType::ElectronForge
|
|
161
|
+
} else if snapshot.electron_dependency.is_some() {
|
|
162
|
+
ProjectType::Electron
|
|
163
|
+
} else if snapshot.package_json.is_some() {
|
|
164
|
+
ProjectType::JavaScript
|
|
165
|
+
} else {
|
|
166
|
+
ProjectType::Unknown
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fn first_script<'a>(snapshot: &project::ProjectSnapshot, names: &'a [&'a str]) -> Option<&'a str> {
|
|
171
|
+
names
|
|
172
|
+
.iter()
|
|
173
|
+
.copied()
|
|
174
|
+
.find(|name| snapshot.scripts.contains_key(*name))
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
fn run_script(snapshot: &project::ProjectSnapshot, script: &str) -> String {
|
|
178
|
+
match snapshot.package_manager.as_deref() {
|
|
179
|
+
Some("bun") => format!("bun run {script}"),
|
|
180
|
+
Some("pnpm") => format!("pnpm run {script}"),
|
|
181
|
+
Some("yarn") => format!("yarn run {script}"),
|
|
182
|
+
Some("npm") | None => format!("npm run {script}"),
|
|
183
|
+
Some(package_manager) => format!("{package_manager} run {script}"),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
fn package_exec(snapshot: &project::ProjectSnapshot, command: &str) -> String {
|
|
188
|
+
match snapshot.package_manager.as_deref() {
|
|
189
|
+
Some("bun") => format!("bunx {command}"),
|
|
190
|
+
Some("pnpm") => format!("pnpm exec {command}"),
|
|
191
|
+
Some("yarn") => format!("yarn {command}"),
|
|
192
|
+
Some("npm") | None => format!("npx {command}"),
|
|
193
|
+
Some(package_manager) => format!("{package_manager} exec {command}"),
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
impl ProjectType {
|
|
198
|
+
fn as_str(&self) -> &'static str {
|
|
199
|
+
match self {
|
|
200
|
+
ProjectType::ElectronForge => "electron-forge",
|
|
201
|
+
ProjectType::Electron => "electron",
|
|
202
|
+
ProjectType::JavaScript => "javascript",
|
|
203
|
+
ProjectType::Unknown => "unknown",
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#[cfg(test)]
|
|
209
|
+
mod tests {
|
|
210
|
+
use std::path::Path;
|
|
211
|
+
|
|
212
|
+
use super::*;
|
|
213
|
+
|
|
214
|
+
#[test]
|
|
215
|
+
fn plans_for_electron_forge_fixture() {
|
|
216
|
+
let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/electron-forge");
|
|
217
|
+
let snapshot = project::inspect(&fixture).expect("fixture should inspect");
|
|
218
|
+
|
|
219
|
+
let report = build_report(&snapshot);
|
|
220
|
+
|
|
221
|
+
assert!(matches!(report.project_type, ProjectType::ElectronForge));
|
|
222
|
+
assert_eq!(
|
|
223
|
+
report.recommended_commands.get("dev").map(String::as_str),
|
|
224
|
+
Some("npm run start")
|
|
225
|
+
);
|
|
226
|
+
assert_eq!(
|
|
227
|
+
report
|
|
228
|
+
.recommended_commands
|
|
229
|
+
.get("package")
|
|
230
|
+
.map(String::as_str),
|
|
231
|
+
Some("npm run package")
|
|
232
|
+
);
|
|
233
|
+
assert!(report.risks.is_empty());
|
|
234
|
+
}
|
|
235
|
+
}
|
package/src/main.rs
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
mod cli;
|
|
2
|
+
mod commands;
|
|
3
|
+
mod output;
|
|
4
|
+
mod project;
|
|
5
|
+
|
|
6
|
+
use anyhow::Result;
|
|
7
|
+
use clap::Parser;
|
|
8
|
+
use cli::{Cli, Commands};
|
|
9
|
+
|
|
10
|
+
fn main() {
|
|
11
|
+
if let Err(error) = run() {
|
|
12
|
+
eprintln!("error: {error:#}");
|
|
13
|
+
std::process::exit(1);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn run() -> Result<()> {
|
|
18
|
+
let cli = Cli::parse();
|
|
19
|
+
|
|
20
|
+
match cli.command {
|
|
21
|
+
Commands::Doctor(args) => commands::doctor::run(args),
|
|
22
|
+
Commands::Inspect(args) => commands::inspect::run(args),
|
|
23
|
+
Commands::Plan(args) => commands::plan::run(args),
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/output.rs
ADDED
package/src/project.rs
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
collections::BTreeMap,
|
|
3
|
+
fs,
|
|
4
|
+
path::{Path, PathBuf},
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
use anyhow::{Context, Result};
|
|
8
|
+
use camino::Utf8PathBuf;
|
|
9
|
+
use serde::Serialize;
|
|
10
|
+
use serde_json::Value;
|
|
11
|
+
|
|
12
|
+
#[derive(Debug, Serialize)]
|
|
13
|
+
pub struct ProjectSnapshot {
|
|
14
|
+
pub root: Utf8PathBuf,
|
|
15
|
+
pub package_json: Option<Utf8PathBuf>,
|
|
16
|
+
pub name: Option<String>,
|
|
17
|
+
pub version: Option<String>,
|
|
18
|
+
pub main: Option<String>,
|
|
19
|
+
pub package_manager: Option<String>,
|
|
20
|
+
pub scripts: BTreeMap<String, String>,
|
|
21
|
+
pub dependencies: BTreeMap<String, String>,
|
|
22
|
+
pub dev_dependencies: BTreeMap<String, String>,
|
|
23
|
+
pub optional_dependencies: BTreeMap<String, String>,
|
|
24
|
+
pub peer_dependencies: BTreeMap<String, String>,
|
|
25
|
+
pub electron_dependency: Option<String>,
|
|
26
|
+
pub forge_dependencies: BTreeMap<String, String>,
|
|
27
|
+
pub signals: Vec<String>,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl ProjectSnapshot {
|
|
31
|
+
pub fn package_label(&self) -> Option<String> {
|
|
32
|
+
match (&self.name, &self.version) {
|
|
33
|
+
(Some(name), Some(version)) => Some(format!("{name}@{version}")),
|
|
34
|
+
(Some(name), None) => Some(name.clone()),
|
|
35
|
+
(None, Some(version)) => Some(format!("version {version}")),
|
|
36
|
+
(None, None) => None,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pub fn has_javascript_dependencies(&self) -> bool {
|
|
41
|
+
!self.dependencies.is_empty()
|
|
42
|
+
|| !self.dev_dependencies.is_empty()
|
|
43
|
+
|| !self.optional_dependencies.is_empty()
|
|
44
|
+
|| !self.peer_dependencies.is_empty()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub fn inspect(cwd: &Path) -> Result<ProjectSnapshot> {
|
|
49
|
+
let cwd = cwd
|
|
50
|
+
.canonicalize()
|
|
51
|
+
.with_context(|| format!("Could not resolve {}", cwd.display()))?;
|
|
52
|
+
|
|
53
|
+
let package_json_path = find_upwards(&cwd, "package.json");
|
|
54
|
+
let root = package_json_path
|
|
55
|
+
.as_ref()
|
|
56
|
+
.and_then(|path| path.parent().map(Path::to_path_buf))
|
|
57
|
+
.unwrap_or(cwd);
|
|
58
|
+
|
|
59
|
+
let package_json = match &package_json_path {
|
|
60
|
+
Some(path) => {
|
|
61
|
+
let raw = fs::read_to_string(path)
|
|
62
|
+
.with_context(|| format!("Could not read {}", path.display()))?;
|
|
63
|
+
Some(
|
|
64
|
+
serde_json::from_str::<Value>(&raw)
|
|
65
|
+
.with_context(|| format!("Could not parse {}", path.display()))?,
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
None => None,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
let scripts = package_json
|
|
72
|
+
.as_ref()
|
|
73
|
+
.map(|package| string_map(package.get("scripts")))
|
|
74
|
+
.unwrap_or_default();
|
|
75
|
+
|
|
76
|
+
let dependencies = package_json
|
|
77
|
+
.as_ref()
|
|
78
|
+
.map(|package| string_map(package.get("dependencies")))
|
|
79
|
+
.unwrap_or_default();
|
|
80
|
+
|
|
81
|
+
let dev_dependencies = package_json
|
|
82
|
+
.as_ref()
|
|
83
|
+
.map(|package| string_map(package.get("devDependencies")))
|
|
84
|
+
.unwrap_or_default();
|
|
85
|
+
|
|
86
|
+
let optional_dependencies = package_json
|
|
87
|
+
.as_ref()
|
|
88
|
+
.map(|package| string_map(package.get("optionalDependencies")))
|
|
89
|
+
.unwrap_or_default();
|
|
90
|
+
|
|
91
|
+
let peer_dependencies = package_json
|
|
92
|
+
.as_ref()
|
|
93
|
+
.map(|package| string_map(package.get("peerDependencies")))
|
|
94
|
+
.unwrap_or_default();
|
|
95
|
+
|
|
96
|
+
let all_dependencies = merge_dependencies([
|
|
97
|
+
&dependencies,
|
|
98
|
+
&dev_dependencies,
|
|
99
|
+
&optional_dependencies,
|
|
100
|
+
&peer_dependencies,
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
let electron_dependency = all_dependencies.get("electron").cloned();
|
|
104
|
+
let forge_dependencies = all_dependencies
|
|
105
|
+
.iter()
|
|
106
|
+
.filter(|(name, _)| name.starts_with("@electron-forge/"))
|
|
107
|
+
.map(|(name, version)| (name.clone(), version.clone()))
|
|
108
|
+
.collect::<BTreeMap<_, _>>();
|
|
109
|
+
|
|
110
|
+
let package_manager = detect_package_manager(&root);
|
|
111
|
+
let signals = build_signals(
|
|
112
|
+
&scripts,
|
|
113
|
+
&all_dependencies,
|
|
114
|
+
electron_dependency.as_ref(),
|
|
115
|
+
&forge_dependencies,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
Ok(ProjectSnapshot {
|
|
119
|
+
root: utf8_path(root)?,
|
|
120
|
+
package_json: package_json_path.map(utf8_path).transpose()?,
|
|
121
|
+
name: package_json
|
|
122
|
+
.as_ref()
|
|
123
|
+
.and_then(|package| package.get("name"))
|
|
124
|
+
.and_then(Value::as_str)
|
|
125
|
+
.map(ToOwned::to_owned),
|
|
126
|
+
version: package_json
|
|
127
|
+
.as_ref()
|
|
128
|
+
.and_then(|package| package.get("version"))
|
|
129
|
+
.and_then(Value::as_str)
|
|
130
|
+
.map(ToOwned::to_owned),
|
|
131
|
+
main: package_json
|
|
132
|
+
.as_ref()
|
|
133
|
+
.and_then(|package| package.get("main"))
|
|
134
|
+
.and_then(Value::as_str)
|
|
135
|
+
.map(ToOwned::to_owned),
|
|
136
|
+
package_manager,
|
|
137
|
+
scripts,
|
|
138
|
+
dependencies,
|
|
139
|
+
dev_dependencies,
|
|
140
|
+
optional_dependencies,
|
|
141
|
+
peer_dependencies,
|
|
142
|
+
electron_dependency,
|
|
143
|
+
forge_dependencies,
|
|
144
|
+
signals,
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fn string_map(value: Option<&Value>) -> BTreeMap<String, String> {
|
|
149
|
+
value
|
|
150
|
+
.and_then(Value::as_object)
|
|
151
|
+
.map(|object| {
|
|
152
|
+
object
|
|
153
|
+
.iter()
|
|
154
|
+
.filter_map(|(key, value)| {
|
|
155
|
+
value.as_str().map(|value| (key.clone(), value.to_string()))
|
|
156
|
+
})
|
|
157
|
+
.collect()
|
|
158
|
+
})
|
|
159
|
+
.unwrap_or_default()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fn merge_dependencies<'a>(
|
|
163
|
+
groups: impl IntoIterator<Item = &'a BTreeMap<String, String>>,
|
|
164
|
+
) -> BTreeMap<String, String> {
|
|
165
|
+
let mut merged = BTreeMap::new();
|
|
166
|
+
|
|
167
|
+
for group in groups {
|
|
168
|
+
for (name, version) in group {
|
|
169
|
+
merged.insert(name.clone(), version.clone());
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
merged
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fn detect_package_manager(root: &Path) -> Option<String> {
|
|
177
|
+
[
|
|
178
|
+
("package-lock.json", "npm"),
|
|
179
|
+
("npm-shrinkwrap.json", "npm"),
|
|
180
|
+
("pnpm-lock.yaml", "pnpm"),
|
|
181
|
+
("yarn.lock", "yarn"),
|
|
182
|
+
("bun.lock", "bun"),
|
|
183
|
+
("bun.lockb", "bun"),
|
|
184
|
+
]
|
|
185
|
+
.iter()
|
|
186
|
+
.find_map(|(file, manager)| root.join(file).exists().then(|| manager.to_string()))
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
fn build_signals(
|
|
190
|
+
scripts: &BTreeMap<String, String>,
|
|
191
|
+
dependencies: &BTreeMap<String, String>,
|
|
192
|
+
electron_dependency: Option<&String>,
|
|
193
|
+
forge_dependencies: &BTreeMap<String, String>,
|
|
194
|
+
) -> Vec<String> {
|
|
195
|
+
let mut signals = Vec::new();
|
|
196
|
+
|
|
197
|
+
if electron_dependency.is_some() {
|
|
198
|
+
signals.push("electron dependency declared".to_string());
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if !forge_dependencies.is_empty() {
|
|
202
|
+
signals.push("electron forge dependency declared".to_string());
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if dependencies.contains_key("vite") || dependencies.contains_key("@vitejs/plugin-react") {
|
|
206
|
+
signals.push("vite tooling detected".to_string());
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if dependencies.contains_key("typescript") {
|
|
210
|
+
signals.push("typescript tooling detected".to_string());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if scripts
|
|
214
|
+
.values()
|
|
215
|
+
.any(|script| script.contains("electron") || script.contains("electron-forge"))
|
|
216
|
+
{
|
|
217
|
+
signals.push("electron command found in package scripts".to_string());
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
signals
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
fn find_upwards(start: &Path, file_name: &str) -> Option<PathBuf> {
|
|
224
|
+
let mut current = Some(start);
|
|
225
|
+
|
|
226
|
+
while let Some(path) = current {
|
|
227
|
+
let candidate = path.join(file_name);
|
|
228
|
+
if candidate.exists() {
|
|
229
|
+
return Some(candidate);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
current = path.parent();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
None
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
fn utf8_path(path: PathBuf) -> Result<Utf8PathBuf> {
|
|
239
|
+
Utf8PathBuf::from_path_buf(path).map_err(|path| {
|
|
240
|
+
anyhow::anyhow!(
|
|
241
|
+
"Path contains invalid UTF-8 and cannot be represented in JSON: {}",
|
|
242
|
+
path.display()
|
|
243
|
+
)
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#[cfg(test)]
|
|
248
|
+
mod tests {
|
|
249
|
+
use super::*;
|
|
250
|
+
|
|
251
|
+
#[test]
|
|
252
|
+
fn maps_string_values_only() {
|
|
253
|
+
let value = serde_json::json!({
|
|
254
|
+
"electron": "^30.0.0",
|
|
255
|
+
"bad": false
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
let map = string_map(Some(&value));
|
|
259
|
+
|
|
260
|
+
assert_eq!(map.get("electron"), Some(&"^30.0.0".to_string()));
|
|
261
|
+
assert!(!map.contains_key("bad"));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
#[test]
|
|
265
|
+
fn builds_electron_signals() {
|
|
266
|
+
let mut scripts = BTreeMap::new();
|
|
267
|
+
scripts.insert("start".to_string(), "electron-forge start".to_string());
|
|
268
|
+
|
|
269
|
+
let mut dependencies = BTreeMap::new();
|
|
270
|
+
dependencies.insert("electron".to_string(), "^30.0.0".to_string());
|
|
271
|
+
dependencies.insert("@electron-forge/cli".to_string(), "^7.0.0".to_string());
|
|
272
|
+
dependencies.insert("typescript".to_string(), "^5.0.0".to_string());
|
|
273
|
+
|
|
274
|
+
let forge = dependencies
|
|
275
|
+
.iter()
|
|
276
|
+
.filter(|(name, _)| name.starts_with("@electron-forge/"))
|
|
277
|
+
.map(|(name, version)| (name.clone(), version.clone()))
|
|
278
|
+
.collect::<BTreeMap<_, _>>();
|
|
279
|
+
|
|
280
|
+
let signals = build_signals(
|
|
281
|
+
&scripts,
|
|
282
|
+
&dependencies,
|
|
283
|
+
dependencies.get("electron"),
|
|
284
|
+
&forge,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
assert!(signals.contains(&"electron dependency declared".to_string()));
|
|
288
|
+
assert!(signals.contains(&"electron forge dependency declared".to_string()));
|
|
289
|
+
assert!(signals.contains(&"typescript tooling detected".to_string()));
|
|
290
|
+
assert!(signals.contains(&"electron command found in package scripts".to_string()));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
#[test]
|
|
294
|
+
fn inspects_electron_forge_fixture_from_nested_directory() {
|
|
295
|
+
let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/electron-forge");
|
|
296
|
+
let nested = fixture.join("src");
|
|
297
|
+
|
|
298
|
+
let snapshot = inspect(&nested).expect("fixture should inspect");
|
|
299
|
+
|
|
300
|
+
assert_eq!(snapshot.name.as_deref(), Some("fixture-electron-forge-app"));
|
|
301
|
+
assert_eq!(snapshot.version.as_deref(), Some("0.1.0"));
|
|
302
|
+
assert_eq!(snapshot.main.as_deref(), Some("src/main.ts"));
|
|
303
|
+
assert_eq!(snapshot.package_manager.as_deref(), Some("npm"));
|
|
304
|
+
assert_eq!(snapshot.electron_dependency.as_deref(), Some("^31.0.0"));
|
|
305
|
+
assert_eq!(
|
|
306
|
+
snapshot
|
|
307
|
+
.forge_dependencies
|
|
308
|
+
.get("@electron-forge/cli")
|
|
309
|
+
.map(String::as_str),
|
|
310
|
+
Some("^7.0.0")
|
|
311
|
+
);
|
|
312
|
+
assert!(snapshot.has_javascript_dependencies());
|
|
313
|
+
assert!(snapshot
|
|
314
|
+
.signals
|
|
315
|
+
.contains(&"electron forge dependency declared".to_string()));
|
|
316
|
+
assert!(snapshot
|
|
317
|
+
.signals
|
|
318
|
+
.contains(&"electron command found in package scripts".to_string()));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fixture-electron-forge-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "fixture-electron-forge-app",
|
|
9
|
+
"version": "0.1.0",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"electron-squirrel-startup": "^1.0.1"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@electron-forge/cli": "^7.0.0",
|
|
15
|
+
"@electron-forge/maker-squirrel": "^7.0.0",
|
|
16
|
+
"@electron-forge/plugin-vite": "^7.0.0",
|
|
17
|
+
"electron": "^31.0.0",
|
|
18
|
+
"typescript": "^5.5.0",
|
|
19
|
+
"vite": "^5.0.0"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fixture-electron-forge-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "src/main.ts",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "electron-forge start",
|
|
7
|
+
"package": "electron-forge package",
|
|
8
|
+
"make": "electron-forge make"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"electron-squirrel-startup": "^1.0.1"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@electron-forge/cli": "^7.0.0",
|
|
15
|
+
"@electron-forge/maker-squirrel": "^7.0.0",
|
|
16
|
+
"@electron-forge/plugin-vite": "^7.0.0",
|
|
17
|
+
"electron": "^31.0.0",
|
|
18
|
+
"typescript": "^5.5.0",
|
|
19
|
+
"vite": "^5.0.0"
|
|
20
|
+
}
|
|
21
|
+
}
|
package/.babelrc
DELETED