@devaloop/devalang 0.0.1-alpha.9 → 0.0.1-beta.2

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.
Files changed (322) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +6 -1
  3. package/.github/workflows/ci.yml +103 -0
  4. package/Cargo.toml +81 -48
  5. package/README.md +137 -154
  6. package/docs/CHANGELOG.md +428 -1
  7. package/docs/CONTRIBUTING.md +101 -0
  8. package/docs/ROADMAP.md +14 -7
  9. package/docs/TODO.md +16 -15
  10. package/examples/automation.deva +42 -0
  11. package/examples/bank.deva +7 -0
  12. package/examples/bus.deva +10 -0
  13. package/examples/duration.deva +9 -0
  14. package/examples/effect.deva +2 -0
  15. package/examples/events.deva +12 -0
  16. package/examples/filter.deva +11 -0
  17. package/examples/function.deva +15 -0
  18. package/examples/index.deva +57 -12
  19. package/examples/lfo.deva +9 -0
  20. package/examples/loop.deva +5 -12
  21. package/examples/pattern.deva +8 -0
  22. package/examples/plugin.deva +16 -0
  23. package/examples/synth.deva +11 -1
  24. package/examples/synth_types.deva +17 -0
  25. package/examples/variables.deva +1 -1
  26. package/out-tsc/bin/index.d.ts +2 -0
  27. package/out-tsc/bin/index.js +51 -7
  28. package/out-tsc/core/functions/index.d.ts +42 -0
  29. package/out-tsc/core/functions/index.js +87 -0
  30. package/out-tsc/core/index.d.ts +6 -0
  31. package/out-tsc/core/index.js +22 -0
  32. package/out-tsc/core/types/index.d.ts +4 -0
  33. package/out-tsc/core/types/index.js +20 -0
  34. package/out-tsc/core/types/plugin.d.ts +18 -0
  35. package/out-tsc/core/types/plugin.js +2 -0
  36. package/out-tsc/core/types/result.d.ts +27 -0
  37. package/out-tsc/core/types/result.js +2 -0
  38. package/out-tsc/core/types/statement.d.ts +106 -0
  39. package/out-tsc/core/types/statement.js +2 -0
  40. package/out-tsc/core/types/value.d.ts +43 -0
  41. package/out-tsc/core/types/value.js +2 -0
  42. package/out-tsc/index.d.ts +7 -0
  43. package/out-tsc/index.js +42 -1
  44. package/out-tsc/pkg/devalang_core.d.ts +15 -0
  45. package/out-tsc/pkg/devalang_core.js +65 -0
  46. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +34 -0
  47. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  48. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  49. package/out-tsc/scripts/postinstall.d.ts +1 -0
  50. package/out-tsc/scripts/postinstall.js +83 -0
  51. package/out-tsc/scripts/version/bump.d.ts +1 -0
  52. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  53. package/out-tsc/scripts/version/index.d.ts +1 -0
  54. package/out-tsc/scripts/version/sync.d.ts +1 -0
  55. package/package.json +28 -7
  56. package/project-version.json +4 -4
  57. package/rust/cli/bank/api.rs +122 -0
  58. package/rust/cli/bank/commands.rs +306 -0
  59. package/rust/cli/bank/mod.rs +29 -0
  60. package/rust/cli/build/commands.rs +153 -0
  61. package/rust/cli/build/mod.rs +2 -0
  62. package/rust/cli/build/process.rs +165 -0
  63. package/rust/cli/check/mod.rs +208 -0
  64. package/rust/cli/discover/commands.rs +253 -0
  65. package/rust/cli/discover/config.rs +111 -0
  66. package/rust/cli/discover/fs.rs +19 -0
  67. package/rust/cli/discover/install.rs +103 -0
  68. package/rust/cli/discover/metadata.rs +48 -0
  69. package/rust/cli/discover/mod.rs +5 -0
  70. package/rust/cli/{init.rs → init/commands.rs} +32 -23
  71. package/rust/cli/init/mod.rs +1 -0
  72. package/rust/cli/install/addon.rs +118 -0
  73. package/rust/cli/install/bank.rs +72 -0
  74. package/rust/cli/install/commands.rs +35 -0
  75. package/rust/cli/install/mod.rs +4 -0
  76. package/rust/cli/install/plugin.rs +80 -0
  77. package/rust/cli/login/commands.rs +124 -0
  78. package/rust/cli/login/mod.rs +1 -0
  79. package/rust/cli/mod.rs +9 -202
  80. package/rust/cli/parser.rs +359 -0
  81. package/rust/cli/play/commands.rs +375 -0
  82. package/rust/cli/play/io.rs +17 -0
  83. package/rust/cli/play/mod.rs +5 -0
  84. package/rust/cli/play/process.rs +159 -0
  85. package/rust/cli/play/realtime.rs +91 -0
  86. package/rust/cli/play/utils.rs +23 -0
  87. package/rust/cli/telemetry/commands.rs +22 -0
  88. package/rust/cli/telemetry/event_creator.rs +80 -0
  89. package/rust/cli/telemetry/mod.rs +3 -0
  90. package/rust/cli/telemetry/send.rs +51 -0
  91. package/rust/cli/{template.rs → template/commands.rs} +17 -5
  92. package/rust/cli/template/mod.rs +1 -0
  93. package/rust/cli/update/commands.rs +6 -0
  94. package/rust/cli/update/mod.rs +1 -0
  95. package/rust/config/driver.rs +112 -0
  96. package/rust/config/mod.rs +3 -16
  97. package/rust/config/ops.rs +26 -0
  98. package/rust/config/settings.rs +101 -0
  99. package/rust/core/audio/engine/driver.rs +220 -0
  100. package/rust/core/audio/engine/export.rs +169 -0
  101. package/rust/core/audio/engine/helpers.rs +178 -0
  102. package/rust/core/audio/engine/mod.rs +56 -0
  103. package/rust/core/audio/engine/notes/dsp.rs +85 -0
  104. package/rust/core/audio/engine/notes/mod.rs +44 -0
  105. package/rust/core/audio/engine/notes/params.rs +294 -0
  106. package/rust/core/audio/engine/sample/insert.rs +199 -0
  107. package/rust/core/audio/engine/sample/mod.rs +40 -0
  108. package/rust/core/audio/engine/sample/padding.rs +170 -0
  109. package/rust/core/audio/evaluator/condition.rs +61 -0
  110. package/rust/core/audio/evaluator/mod.rs +9 -0
  111. package/rust/core/audio/evaluator/numeric.rs +152 -0
  112. package/rust/core/audio/evaluator/rhs.rs +16 -0
  113. package/rust/core/audio/evaluator/string_expr.rs +94 -0
  114. package/rust/core/audio/interpreter/driver.rs +574 -216
  115. package/rust/core/audio/interpreter/mod.rs +2 -12
  116. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +175 -0
  117. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +384 -0
  118. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +2 -0
  119. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +316 -0
  120. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
  121. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
  122. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
  123. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
  124. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
  125. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
  126. package/rust/core/audio/interpreter/statements/automate.rs +16 -0
  127. package/rust/core/audio/interpreter/statements/call.rs +295 -0
  128. package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +72 -69
  129. package/rust/core/audio/interpreter/statements/function.rs +24 -0
  130. package/rust/core/audio/interpreter/statements/let_.rs +36 -0
  131. package/rust/core/audio/interpreter/statements/load.rs +17 -0
  132. package/rust/core/audio/interpreter/statements/loop_.rs +115 -0
  133. package/rust/core/audio/interpreter/statements/mod.rs +12 -0
  134. package/rust/core/audio/interpreter/statements/sleep.rs +28 -0
  135. package/rust/core/audio/interpreter/statements/spawn.rs +253 -0
  136. package/rust/core/audio/interpreter/statements/tempo.rs +40 -0
  137. package/rust/core/audio/interpreter/statements/trigger.rs +239 -0
  138. package/rust/core/audio/loader/mod.rs +1 -1
  139. package/rust/core/audio/loader/trigger.rs +98 -52
  140. package/rust/core/audio/mod.rs +2 -2
  141. package/rust/core/audio/player.rs +28 -12
  142. package/rust/core/audio/special/easing.rs +189 -0
  143. package/rust/core/audio/special/env.rs +45 -0
  144. package/rust/core/audio/special/math.rs +134 -0
  145. package/rust/core/audio/special/mod.rs +9 -0
  146. package/rust/core/audio/special/modulator.rs +143 -0
  147. package/rust/core/builder/mod.rs +129 -80
  148. package/rust/core/debugger/lexer.rs +4 -4
  149. package/rust/core/debugger/logs.rs +52 -0
  150. package/rust/core/debugger/mod.rs +11 -2
  151. package/rust/core/debugger/preprocessor.rs +4 -4
  152. package/rust/core/debugger/store.rs +38 -25
  153. package/rust/core/error/mod.rs +221 -12
  154. package/rust/core/lexer/driver.rs +59 -0
  155. package/rust/core/lexer/handler/arrow.rs +62 -11
  156. package/rust/core/lexer/handler/at.rs +5 -5
  157. package/rust/core/lexer/handler/brace.rs +11 -11
  158. package/rust/core/lexer/handler/colon.rs +5 -5
  159. package/rust/core/lexer/handler/comment.rs +3 -3
  160. package/rust/core/lexer/handler/dot.rs +6 -6
  161. package/rust/core/lexer/handler/driver.rs +143 -32
  162. package/rust/core/lexer/handler/identifier.rs +11 -5
  163. package/rust/core/lexer/handler/indent.rs +18 -4
  164. package/rust/core/lexer/handler/mod.rs +6 -5
  165. package/rust/core/lexer/handler/newline.rs +3 -3
  166. package/rust/core/lexer/handler/number.rs +5 -5
  167. package/rust/core/lexer/handler/operator.rs +5 -3
  168. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  169. package/rust/core/lexer/handler/slash.rs +21 -0
  170. package/rust/core/lexer/handler/string.rs +3 -3
  171. package/rust/core/lexer/mod.rs +1 -49
  172. package/rust/core/lexer/token.rs +17 -12
  173. package/rust/core/mod.rs +9 -10
  174. package/rust/core/parser/driver/block.rs +111 -0
  175. package/rust/core/parser/driver/cursor.rs +82 -0
  176. package/rust/core/parser/driver/driver_impl.rs +139 -0
  177. package/rust/core/parser/driver/mod.rs +6 -0
  178. package/rust/core/parser/driver/parse_array.rs +120 -0
  179. package/rust/core/parser/driver/parse_map.rs +223 -0
  180. package/rust/core/parser/driver/parser.rs +160 -0
  181. package/rust/core/parser/handler/arrow_call.rs +277 -126
  182. package/rust/core/parser/handler/at.rs +142 -25
  183. package/rust/core/parser/handler/bank.rs +83 -20
  184. package/rust/core/parser/handler/condition.rs +14 -5
  185. package/rust/core/parser/handler/dot.rs +111 -75
  186. package/rust/core/parser/handler/identifier/automate.rs +254 -0
  187. package/rust/core/parser/handler/identifier/call.rs +74 -24
  188. package/rust/core/parser/handler/identifier/emit.rs +70 -0
  189. package/rust/core/parser/handler/identifier/function.rs +113 -0
  190. package/rust/core/parser/handler/identifier/group.rs +28 -14
  191. package/rust/core/parser/handler/identifier/let_.rs +61 -21
  192. package/rust/core/parser/handler/identifier/mod.rs +24 -20
  193. package/rust/core/parser/handler/identifier/on.rs +107 -0
  194. package/rust/core/parser/handler/identifier/print.rs +49 -0
  195. package/rust/core/parser/handler/identifier/sleep.rs +77 -14
  196. package/rust/core/parser/handler/identifier/spawn.rs +81 -31
  197. package/rust/core/parser/handler/identifier/synth.rs +102 -32
  198. package/rust/core/parser/handler/loop_.rs +144 -22
  199. package/rust/core/parser/handler/mod.rs +6 -5
  200. package/rust/core/parser/handler/pattern.rs +74 -0
  201. package/rust/core/parser/handler/tempo.rs +67 -9
  202. package/rust/core/parser/mod.rs +3 -4
  203. package/rust/core/parser/statement.rs +6 -92
  204. package/rust/core/plugin/loader.rs +137 -0
  205. package/rust/core/plugin/mod.rs +2 -0
  206. package/rust/core/plugin/runner/mod.rs +11 -0
  207. package/rust/core/plugin/runner/non_wasm.rs +297 -0
  208. package/rust/core/plugin/runner/wasm32.rs +43 -0
  209. package/rust/core/preprocessor/loader/inject.rs +278 -0
  210. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
  211. package/rust/core/preprocessor/loader/mod.rs +235 -0
  212. package/rust/core/preprocessor/mod.rs +4 -4
  213. package/rust/core/preprocessor/module.rs +55 -50
  214. package/rust/core/preprocessor/processor/handlers.rs +107 -0
  215. package/rust/core/preprocessor/processor/mod.rs +1 -0
  216. package/rust/core/preprocessor/resolver/bank.rs +14 -12
  217. package/rust/core/preprocessor/resolver/call.rs +106 -105
  218. package/rust/core/preprocessor/resolver/condition.rs +13 -10
  219. package/rust/core/preprocessor/resolver/driver.rs +145 -48
  220. package/rust/core/preprocessor/resolver/function.rs +69 -0
  221. package/rust/core/preprocessor/resolver/group.rs +122 -61
  222. package/rust/core/preprocessor/resolver/let_.rs +13 -12
  223. package/rust/core/preprocessor/resolver/loop_.rs +240 -13
  224. package/rust/core/preprocessor/resolver/mod.rs +8 -6
  225. package/rust/core/preprocessor/resolver/pattern.rs +83 -0
  226. package/rust/core/preprocessor/resolver/spawn.rs +83 -42
  227. package/rust/core/preprocessor/resolver/synth.rs +15 -11
  228. package/rust/core/preprocessor/resolver/tempo.rs +13 -14
  229. package/rust/core/preprocessor/resolver/trigger.rs +32 -28
  230. package/rust/core/preprocessor/resolver/value.rs +111 -13
  231. package/rust/core/store/global.rs +57 -39
  232. package/rust/core/store/mod.rs +0 -3
  233. package/rust/lib.rs +323 -117
  234. package/rust/main.rs +388 -65
  235. package/rust/types/Cargo.toml +11 -0
  236. package/rust/types/src/addons.rs +55 -0
  237. package/rust/types/src/ast.rs +202 -0
  238. package/rust/types/src/config.rs +84 -0
  239. package/rust/types/src/lib.rs +15 -0
  240. package/rust/types/src/plugin.rs +20 -0
  241. package/rust/types/src/store.rs +139 -0
  242. package/rust/types/src/telemetry.rs +85 -0
  243. package/rust/utils/Cargo.toml +26 -0
  244. package/rust/utils/src/error.rs +186 -0
  245. package/rust/utils/src/file.rs +94 -0
  246. package/rust/utils/src/first_usage.rs +97 -0
  247. package/rust/utils/{mod.rs → src/lib.rs} +6 -3
  248. package/rust/utils/{logger.rs → src/logger.rs} +94 -17
  249. package/rust/utils/src/path.rs +129 -0
  250. package/rust/utils/src/signature.rs +41 -0
  251. package/rust/utils/{spinner.rs → src/spinner.rs} +7 -8
  252. package/rust/utils/src/version.rs +27 -0
  253. package/rust/utils/{watcher.rs → src/watcher.rs} +17 -4
  254. package/rust/web/api.rs +5 -0
  255. package/rust/web/cdn.rs +34 -0
  256. package/rust/web/mod.rs +3 -0
  257. package/rust/web/sso.rs +5 -0
  258. package/templates/minimal/README.md +143 -127
  259. package/templates/welcome/README.md +143 -127
  260. package/templates/welcome/src/index.deva +56 -8
  261. package/templates/welcome/src/variables.deva +2 -4
  262. package/tests/integration.rs +21 -0
  263. package/tests/rust/cli_check_build.rs +21 -0
  264. package/tests/rust/cli_help.rs +12 -0
  265. package/tests/rust/cli_template_list.rs +10 -0
  266. package/tests/rust/cli_version.rs +11 -0
  267. package/tests/typescript/index.spec.ts +136 -0
  268. package/tests/typescript/playhead.spec.ts +36 -0
  269. package/tests/typescript/render_e2e.spec.ts +77 -0
  270. package/tsconfig.json +12 -10
  271. package/typescript/bin/index.ts +19 -5
  272. package/typescript/core/functions/index.ts +94 -0
  273. package/typescript/core/index.ts +6 -0
  274. package/typescript/core/types/index.ts +4 -0
  275. package/typescript/core/types/plugin.ts +19 -0
  276. package/typescript/core/types/result.ts +29 -0
  277. package/typescript/core/types/statement.ts +47 -0
  278. package/typescript/core/types/value.ts +29 -0
  279. package/typescript/index.ts +8 -1
  280. package/typescript/pkg/devalang_core.d.ts +4 -0
  281. package/typescript/pkg/devalang_core.ts +65 -0
  282. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  283. package/typescript/scripts/postinstall.ts +85 -0
  284. package/typescript/scripts/version/bump.ts +0 -1
  285. package/typescript/scripts/version/index.ts +0 -1
  286. package/docs/COMMANDS.md +0 -85
  287. package/docs/CONFIG.md +0 -30
  288. package/docs/SYNTAX.md +0 -210
  289. package/out-tsc/bin/devalang.exe +0 -0
  290. package/out-tsc/scripts/postbuild.js +0 -11
  291. package/rust/cli/build.rs +0 -137
  292. package/rust/cli/check.rs +0 -117
  293. package/rust/cli/play.rs +0 -193
  294. package/rust/config/loader.rs +0 -13
  295. package/rust/core/audio/engine.rs +0 -203
  296. package/rust/core/audio/evaluator.rs +0 -31
  297. package/rust/core/audio/interpreter/arrow_call.rs +0 -129
  298. package/rust/core/audio/interpreter/call.rs +0 -64
  299. package/rust/core/audio/interpreter/let_.rs +0 -19
  300. package/rust/core/audio/interpreter/load.rs +0 -18
  301. package/rust/core/audio/interpreter/loop_.rs +0 -67
  302. package/rust/core/audio/interpreter/sleep.rs +0 -36
  303. package/rust/core/audio/interpreter/spawn.rs +0 -66
  304. package/rust/core/audio/interpreter/tempo.rs +0 -16
  305. package/rust/core/audio/interpreter/trigger.rs +0 -69
  306. package/rust/core/audio/renderer.rs +0 -54
  307. package/rust/core/parser/driver.rs +0 -331
  308. package/rust/core/preprocessor/loader.rs +0 -193
  309. package/rust/core/preprocessor/processor.rs +0 -76
  310. package/rust/core/shared/duration.rs +0 -8
  311. package/rust/core/shared/mod.rs +0 -2
  312. package/rust/core/shared/value.rs +0 -18
  313. package/rust/core/store/export.rs +0 -28
  314. package/rust/core/store/import.rs +0 -28
  315. package/rust/core/store/variable.rs +0 -28
  316. package/rust/core/utils/mod.rs +0 -2
  317. package/rust/core/utils/path.rs +0 -31
  318. package/rust/core/utils/validation.rs +0 -37
  319. package/rust/utils/file.rs +0 -35
  320. package/rust/utils/signature.rs +0 -17
  321. package/rust/utils/version.rs +0 -15
  322. package/typescript/scripts/postbuild.ts +0 -8
@@ -0,0 +1,165 @@
1
+ use crate::core::{
2
+ builder::Builder,
3
+ debugger::{
4
+ lexer::write_lexer_log_file,
5
+ logs::{write_module_function_log_file, write_module_variable_log_file},
6
+ preprocessor::write_preprocessor_log_file,
7
+ store::{write_function_log_file, write_variables_log_file},
8
+ },
9
+ preprocessor::loader::ModuleLoader,
10
+ store::global::GlobalStore,
11
+ };
12
+ use devalang_utils::path::normalize_path;
13
+ use devalang_utils::{
14
+ logger::{LogLevel, Logger},
15
+ spinner::start_spinner,
16
+ };
17
+
18
+ pub struct BuildStatsInput {
19
+ pub statements_by_module:
20
+ std::collections::HashMap<String, Vec<crate::core::parser::statement::Statement>>,
21
+ pub global_store: crate::core::store::global::GlobalStore,
22
+ }
23
+
24
+ pub fn process_build(
25
+ entry: String,
26
+ output: String,
27
+ output_format: Vec<crate::cli::parser::OutputFormat>,
28
+ audio_format: crate::cli::parser::AudioFormat,
29
+ sample_rate: u32,
30
+ debug: bool,
31
+ compress: bool,
32
+ ) -> Result<BuildStatsInput, String> {
33
+ let spinner = start_spinner("Building...");
34
+
35
+ let duration = std::time::Instant::now();
36
+
37
+ let normalized_entry_file = normalize_path(&entry);
38
+ let normalized_output_dir = normalize_path(&output);
39
+
40
+ let mut global_store = GlobalStore::new();
41
+ let module_loader = ModuleLoader::new(&normalized_entry_file, &normalized_output_dir);
42
+
43
+ // SECTION Load
44
+ // NOTE: We use modules in the build command, so we need to load them
45
+ let (modules_tokens, modules_statements) = module_loader.load_all_modules(&mut global_store);
46
+
47
+ // SECTION Write logs
48
+ if debug {
49
+ for (module_path, module) in global_store.modules.clone() {
50
+ write_module_variable_log_file(
51
+ &normalized_output_dir,
52
+ &module_path,
53
+ &module.variable_table,
54
+ );
55
+ write_module_function_log_file(
56
+ &normalized_output_dir,
57
+ &module_path,
58
+ &module.function_table,
59
+ );
60
+ }
61
+
62
+ write_lexer_log_file(
63
+ &normalized_output_dir,
64
+ "lexer_tokens.log",
65
+ modules_tokens.clone(),
66
+ );
67
+ write_preprocessor_log_file(
68
+ &normalized_output_dir,
69
+ "resolved_statements.log",
70
+ modules_statements.clone(),
71
+ );
72
+ write_variables_log_file(
73
+ &normalized_output_dir,
74
+ "global_variables.log",
75
+ global_store.variables.clone(),
76
+ );
77
+ write_function_log_file(
78
+ &normalized_output_dir,
79
+ "global_functions.log",
80
+ global_store.functions.clone(),
81
+ );
82
+ }
83
+
84
+ // SECTION Detect build-time errors prior to building
85
+ let all_errors = crate::core::error::collect_all_errors_with_modules(&modules_statements);
86
+ let (warnings, criticals) = crate::core::error::partition_errors(all_errors);
87
+ crate::core::error::log_errors_with_stack("Build", &warnings, &criticals);
88
+ if !criticals.is_empty() {
89
+ spinner.finish_and_clear();
90
+ return Err(format!(
91
+ "build failed with {} critical error(s): {}",
92
+ criticals.len(),
93
+ criticals[0].message
94
+ ));
95
+ }
96
+
97
+ // SECTION Building AST and Audio
98
+ let builder = Builder::new();
99
+ builder.build_ast(&modules_statements, &normalized_output_dir, compress);
100
+
101
+ // generate audio output if requested
102
+ if output_format.contains(&crate::cli::parser::OutputFormat::Wav) {
103
+ let audio_format_str = format!("{:?}", audio_format);
104
+ builder.build_audio(
105
+ &modules_statements,
106
+ &normalized_output_dir,
107
+ &mut global_store,
108
+ Some(audio_format_str),
109
+ Some(sample_rate),
110
+ );
111
+ }
112
+
113
+ // generate midi output if requested
114
+ if output_format.contains(&crate::cli::parser::OutputFormat::Mid) {
115
+ builder.build_midi(
116
+ &modules_statements,
117
+ &normalized_output_dir,
118
+ &mut global_store,
119
+ );
120
+ }
121
+
122
+ // SECTION Logging
123
+ let logger = Logger::new();
124
+
125
+ if debug {
126
+ let modules_loaded = global_store.modules.keys().collect::<Vec<_>>();
127
+ let global_variables_loaded = global_store.variables.variables.keys().collect::<Vec<_>>();
128
+ let global_functions_loaded = global_store.functions.functions.keys().collect::<Vec<_>>();
129
+
130
+ logger.log_message_with_trace(
131
+ LogLevel::Debug,
132
+ &format!("Modules loaded: {}", global_store.modules.len()),
133
+ modules_loaded.iter().map(|s| s.as_str()).collect(),
134
+ );
135
+ logger.log_message_with_trace(
136
+ LogLevel::Debug,
137
+ &format!(
138
+ "Global variables: {}",
139
+ global_store.variables.variables.len()
140
+ ),
141
+ global_variables_loaded.iter().map(|s| s.as_str()).collect(),
142
+ );
143
+ logger.log_message_with_trace(
144
+ LogLevel::Debug,
145
+ &format!(
146
+ "Global functions: {}",
147
+ global_store.functions.functions.len()
148
+ ),
149
+ global_functions_loaded.iter().map(|s| s.as_str()).collect(),
150
+ );
151
+ }
152
+
153
+ let success_message = format!(
154
+ "Build completed successfully in {:.2?}. Output files written to: '{}'",
155
+ duration.elapsed(),
156
+ normalized_output_dir
157
+ );
158
+
159
+ spinner.finish_and_clear();
160
+ logger.log_message(LogLevel::Success, &success_message);
161
+ Ok(BuildStatsInput {
162
+ statements_by_module: modules_statements,
163
+ global_store,
164
+ })
165
+ }
@@ -0,0 +1,208 @@
1
+ use crate::{
2
+ config::driver::ProjectConfig,
3
+ core::{
4
+ debugger::{
5
+ lexer::write_lexer_log_file,
6
+ logs::{write_module_function_log_file, write_module_variable_log_file},
7
+ preprocessor::write_preprocessor_log_file,
8
+ store::{write_function_log_file, write_variables_log_file},
9
+ },
10
+ preprocessor::loader::ModuleLoader,
11
+ store::global::GlobalStore,
12
+ },
13
+ };
14
+ use devalang_utils::path::{find_entry_file, normalize_path};
15
+
16
+ use devalang_utils::{
17
+ logger::{LogLevel, Logger},
18
+ spinner::start_spinner,
19
+ watcher::watch_directory,
20
+ };
21
+
22
+ #[cfg(feature = "cli")]
23
+ pub fn handle_check_command(
24
+ config: Option<ProjectConfig>,
25
+ entry: Option<String>,
26
+ output: Option<String>,
27
+ watch: bool,
28
+ debug: bool,
29
+ ) -> Result<(), String> {
30
+ let fetched_entry = if entry.is_none() {
31
+ config
32
+ .as_ref()
33
+ .and_then(|c| c.defaults.entry.clone())
34
+ .unwrap_or_default()
35
+ } else {
36
+ entry.clone().unwrap_or_default()
37
+ };
38
+
39
+ let fetched_output = if output.is_none() {
40
+ config
41
+ .as_ref()
42
+ .and_then(|c| c.defaults.output.clone())
43
+ .unwrap_or_default()
44
+ } else {
45
+ output.clone().unwrap_or_default()
46
+ };
47
+
48
+ let fetched_watch = if watch {
49
+ watch
50
+ } else {
51
+ config
52
+ .as_ref()
53
+ .and_then(|c| c.defaults.watch)
54
+ .unwrap_or(false)
55
+ };
56
+
57
+ let logger = Logger::new();
58
+
59
+ if fetched_entry.is_empty() {
60
+ logger.log_message(
61
+ LogLevel::Error,
62
+ "Entry path is not specified. Please provide a valid entry path.",
63
+ );
64
+ return Err("missing entry path".to_string());
65
+ }
66
+ if fetched_output.is_empty() {
67
+ logger.log_message(
68
+ LogLevel::Error,
69
+ "Output directory is not specified. Please provide a valid output directory.",
70
+ );
71
+ return Err("missing output directory".to_string());
72
+ }
73
+
74
+ let entry_file = match find_entry_file(&fetched_entry) {
75
+ Some(p) => p,
76
+ None => {
77
+ logger.log_message(
78
+ LogLevel::Error,
79
+ &format!("❌ index.deva not found in directory: {}", fetched_entry),
80
+ );
81
+ return Err("index.deva not found".to_string());
82
+ }
83
+ };
84
+
85
+ // SECTION Begin check
86
+ if fetched_watch {
87
+ let _ = begin_check(
88
+ entry_file.clone(),
89
+ fetched_output.clone(),
90
+ debug,
91
+ config.clone(),
92
+ );
93
+
94
+ logger.log_message(
95
+ LogLevel::Watcher,
96
+ &format!("Watching for changes in '{}'...", fetched_entry),
97
+ );
98
+
99
+ let cfg_for_watch = config.clone();
100
+ watch_directory(entry_file.clone(), move || {
101
+ logger.log_message(LogLevel::Watcher, "Detected changes, re-checking...");
102
+ if let Err(e) = begin_check(
103
+ entry_file.clone(),
104
+ fetched_output.clone(),
105
+ debug,
106
+ cfg_for_watch.clone(),
107
+ ) {
108
+ eprintln!("[check] failed: {}", e);
109
+ }
110
+ })
111
+ .unwrap();
112
+ } else {
113
+ begin_check(
114
+ entry_file.clone(),
115
+ fetched_output.clone(),
116
+ debug,
117
+ config.clone(),
118
+ )?;
119
+ }
120
+ Ok(())
121
+ }
122
+
123
+ fn begin_check(
124
+ entry: String,
125
+ output: String,
126
+ debug: bool,
127
+ _config: Option<ProjectConfig>,
128
+ ) -> Result<(), String> {
129
+ let spinner = start_spinner("Checking...");
130
+
131
+ let duration = std::time::Instant::now();
132
+
133
+ let normalized_entry_file = normalize_path(&entry);
134
+ let normalized_output_dir = normalize_path(&output);
135
+
136
+ let mut global_store = GlobalStore::new();
137
+ let module_loader = ModuleLoader::new(&normalized_entry_file, &normalized_output_dir);
138
+
139
+ // SECTION Load
140
+ // NOTE: We don't use modules in the check command, but we still need to load them
141
+ let modules = module_loader.load_all_modules(&mut global_store);
142
+
143
+ // Debugging: Log loaded modules and errors
144
+ let logger = Logger::new();
145
+ logger.log_message(LogLevel::Info, "Loaded modules:");
146
+ for module_name in modules.0.keys() {
147
+ logger.log_message(LogLevel::Info, &format!("- {}", module_name));
148
+ }
149
+
150
+ if debug {
151
+ for (module_path, module) in global_store.modules.clone() {
152
+ write_module_variable_log_file(
153
+ &normalized_output_dir,
154
+ &module_path,
155
+ &module.variable_table,
156
+ );
157
+ write_module_function_log_file(
158
+ &normalized_output_dir,
159
+ &module_path,
160
+ &module.function_table,
161
+ );
162
+ }
163
+
164
+ write_lexer_log_file(
165
+ &normalized_output_dir,
166
+ "lexer_tokens.log",
167
+ modules.0.clone(),
168
+ );
169
+ write_preprocessor_log_file(
170
+ &normalized_output_dir,
171
+ "resolved_statements.log",
172
+ modules.1.clone(),
173
+ );
174
+ write_variables_log_file(
175
+ &normalized_output_dir,
176
+ "global_variables.log",
177
+ global_store.variables.clone(),
178
+ );
179
+ write_function_log_file(
180
+ &normalized_output_dir,
181
+ "global_functions.log",
182
+ global_store.functions.clone(),
183
+ );
184
+ }
185
+
186
+ let all_errors = crate::core::error::collect_all_errors_with_modules(&modules.1);
187
+
188
+ let (warnings, criticals) = crate::core::error::partition_errors(all_errors);
189
+ crate::core::error::log_errors_with_stack("Check", &warnings, &criticals);
190
+
191
+ if !criticals.is_empty() {
192
+ spinner.finish_and_clear();
193
+ return Err("check failed with critical errors".to_string());
194
+ } else {
195
+ logger.log_message(LogLevel::Success, "No errors detected.");
196
+
197
+ let success_message = format!(
198
+ "Check completed successfully in {:.2?}. Output files written to: '{}'",
199
+ duration.elapsed(),
200
+ normalized_output_dir
201
+ );
202
+
203
+ spinner.finish_and_clear();
204
+ logger.log_message(LogLevel::Success, &success_message);
205
+ }
206
+
207
+ Ok(())
208
+ }
@@ -0,0 +1,253 @@
1
+ use crate::cli::discover::config::add_addons_to_config;
2
+ use crate::cli::discover::install::install_selected_addons;
3
+ use devalang_types::DiscoveredAddon;
4
+ use devalang_utils::{
5
+ logger::{LogLevel, Logger},
6
+ path as path_utils,
7
+ spinner::start_spinner,
8
+ };
9
+
10
+ pub async fn handle_discover_command() -> Result<(), String> {
11
+ let deva_dir = path_utils::ensure_deva_dir()?;
12
+
13
+ // Search for addons (banks, plugins, presets, templates) in the .deva directory
14
+ let valid_addons_extensions = ["devabank", "devaplugin", "devapreset", "devatemplate"];
15
+
16
+ let mut addons_found = Vec::new();
17
+
18
+ // Recursively walk the .deva directory and collect addon files matching
19
+ // the known addon extensions. This allows discovery in nested folders.
20
+ fn walk_dir_collect(base: &std::path::Path, exts: &[&str], out: &mut Vec<DiscoveredAddon>) {
21
+ if let Ok(entries) = std::fs::read_dir(base) {
22
+ for entry in entries.filter_map(|e| e.ok()) {
23
+ let p = entry.path();
24
+ if p.is_dir() {
25
+ walk_dir_collect(&p, exts, out);
26
+ } else if p.is_file() {
27
+ if let Some(ext) = p.extension().and_then(|s| s.to_str()) {
28
+ if exts.contains(&ext) {
29
+ let name = p
30
+ .file_stem()
31
+ .map(|s| s.to_string_lossy().to_string())
32
+ .unwrap_or_default();
33
+ let addon_type = match ext {
34
+ "devabank" => "bank",
35
+ "devaplugin" => "plugin",
36
+ "devapreset" => "preset",
37
+ "devatemplate" => "template",
38
+ _ => "unknown",
39
+ };
40
+
41
+ out.push(DiscoveredAddon {
42
+ path: p.clone(),
43
+ name,
44
+ extension: ext.to_string(),
45
+ addon_type: addon_type.to_string(),
46
+ });
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ walk_dir_collect(&deva_dir, &valid_addons_extensions, &mut addons_found);
55
+
56
+ let logger = Logger::new();
57
+
58
+ let banks_found = addons_found
59
+ .iter()
60
+ .filter(|addon| addon.addon_type == "bank")
61
+ .cloned()
62
+ .collect::<Vec<_>>();
63
+
64
+ let plugins_found = addons_found
65
+ .iter()
66
+ .filter(|addon| addon.addon_type == "plugin")
67
+ .cloned()
68
+ .collect::<Vec<_>>();
69
+
70
+ let presets_found = addons_found
71
+ .iter()
72
+ .filter(|addon| addon.addon_type == "preset")
73
+ .cloned()
74
+ .collect::<Vec<_>>();
75
+
76
+ let templates_found = addons_found
77
+ .iter()
78
+ .filter(|addon| addon.addon_type == "template")
79
+ .cloned()
80
+ .collect::<Vec<_>>();
81
+
82
+ let mut all_addons = Vec::with_capacity(
83
+ banks_found.len() + plugins_found.len() + presets_found.len() + templates_found.len(),
84
+ );
85
+ all_addons.extend(banks_found.iter().cloned());
86
+ all_addons.extend(plugins_found.iter().cloned());
87
+ all_addons.extend(presets_found.iter().cloned());
88
+ all_addons.extend(templates_found.iter().cloned());
89
+
90
+ println!();
91
+
92
+ if all_addons.is_empty() {
93
+ logger.log_message(LogLevel::Error, "No addons found in the '.deva' folder");
94
+ return Ok(());
95
+ }
96
+
97
+ if !banks_found.is_empty() {
98
+ let mut bank_traces: Vec<String> = Vec::new();
99
+
100
+ for addon in &banks_found {
101
+ bank_traces.push(addon.name.to_string());
102
+ }
103
+
104
+ let trace_refs: Vec<&str> = bank_traces.iter().map(|s| s.as_str()).collect();
105
+
106
+ logger.log_message_with_trace(
107
+ LogLevel::Info,
108
+ format!("Found {} compiled banks in workspace", banks_found.len()).as_str(),
109
+ trace_refs,
110
+ );
111
+ }
112
+
113
+ if !plugins_found.is_empty() {
114
+ let mut plugin_traces: Vec<String> = Vec::new();
115
+
116
+ for addon in &plugins_found {
117
+ plugin_traces.push(addon.name.to_string());
118
+ }
119
+
120
+ let trace_refs: Vec<&str> = plugin_traces.iter().map(|s| s.as_str()).collect();
121
+
122
+ logger.log_message_with_trace(
123
+ LogLevel::Info,
124
+ format!(
125
+ "Found {} compiled plugins in workspace",
126
+ plugins_found.len()
127
+ )
128
+ .as_str(),
129
+ trace_refs,
130
+ );
131
+ }
132
+
133
+ if !presets_found.is_empty() {
134
+ let mut preset_traces: Vec<String> = Vec::new();
135
+
136
+ for addon in &presets_found {
137
+ preset_traces.push(addon.name.to_string());
138
+ }
139
+
140
+ let trace_refs: Vec<&str> = preset_traces.iter().map(|s| s.as_str()).collect();
141
+
142
+ logger.log_message_with_trace(
143
+ LogLevel::Info,
144
+ format!(
145
+ "Found {} compiled presets in workspace",
146
+ presets_found.len()
147
+ )
148
+ .as_str(),
149
+ trace_refs,
150
+ );
151
+ }
152
+
153
+ if !templates_found.is_empty() {
154
+ let mut template_traces: Vec<String> = Vec::new();
155
+
156
+ for addon in &templates_found {
157
+ template_traces.push(addon.name.to_string());
158
+ }
159
+
160
+ let trace_refs: Vec<&str> = template_traces.iter().map(|s| s.as_str()).collect();
161
+
162
+ logger.log_message_with_trace(
163
+ LogLevel::Info,
164
+ format!(
165
+ "Found {} compiled templates in workspace",
166
+ templates_found.len()
167
+ )
168
+ .as_str(),
169
+ trace_refs,
170
+ );
171
+ }
172
+
173
+ println!();
174
+
175
+ // Build user-friendly, unique labels tied to each addon by including the path
176
+ let choice_labels: Vec<String> = all_addons
177
+ .iter()
178
+ .map(|addon| {
179
+ format!(
180
+ "{}: {} ({})",
181
+ addon.addon_type,
182
+ addon.name,
183
+ addon.path.display()
184
+ )
185
+ })
186
+ .collect();
187
+
188
+ let selected_addons = match inquire::MultiSelect::new(
189
+ "Select addons to install:",
190
+ choice_labels.clone(),
191
+ )
192
+ .prompt()
193
+ {
194
+ Ok(selected_addons) => selected_addons,
195
+ Err(err) => {
196
+ logger.log_message(
197
+ LogLevel::Error,
198
+ format!("Error selecting addons: {}", err).as_str(),
199
+ );
200
+
201
+ return Err(format!("Error selecting addons: {}", err));
202
+ }
203
+ };
204
+
205
+ let spinner = start_spinner("Installing addons...");
206
+
207
+ let addons_to_install = selected_addons
208
+ .iter()
209
+ .filter_map(|label| {
210
+ all_addons.iter().find(|addon| {
211
+ let candidate = format!(
212
+ "{}: {} ({})",
213
+ addon.addon_type,
214
+ addon.name,
215
+ addon.path.display()
216
+ );
217
+ &candidate == label
218
+ })
219
+ })
220
+ .cloned()
221
+ .collect::<Vec<_>>();
222
+
223
+ let install_selected_addons_result = install_selected_addons(addons_to_install).await;
224
+ match install_selected_addons_result {
225
+ Ok(addons_enriched) => {
226
+ if let Err(e) = add_addons_to_config(addons_enriched).await {
227
+ spinner.finish_and_clear();
228
+ logger.log_message(
229
+ LogLevel::Error,
230
+ format!("Failed to add addons to config: {}", e).as_str(),
231
+ );
232
+ return Err(format!("Failed to add addons to config: {}", e));
233
+ }
234
+
235
+ spinner.finish_and_clear();
236
+ println!();
237
+ logger.log_message(
238
+ LogLevel::Success,
239
+ "Successfully installed addons !".to_string().as_str(),
240
+ );
241
+ }
242
+ Err(e) => {
243
+ spinner.finish_and_clear();
244
+ logger.log_message(
245
+ LogLevel::Error,
246
+ format!("Failed to install addons: {}", e).as_str(),
247
+ );
248
+ return Err(format!("Failed to install addons: {}", e));
249
+ }
250
+ }
251
+
252
+ Ok(())
253
+ }