@devaloop/devalang 0.0.1-alpha.1 → 0.0.1-alpha.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 (49) hide show
  1. package/.devalang +4 -0
  2. package/Cargo.toml +3 -2
  3. package/README.md +30 -6
  4. package/docs/CHANGELOG.md +53 -0
  5. package/docs/COMMANDS.md +29 -6
  6. package/docs/CONFIG.md +28 -0
  7. package/docs/ROADMAP.md +1 -1
  8. package/docs/TODO.md +43 -15
  9. package/examples/index.deva +1 -1
  10. package/out-tsc/bin/devalang.exe +0 -0
  11. package/package.json +2 -2
  12. package/project-version.json +3 -3
  13. package/rust/cli/build.rs +58 -5
  14. package/rust/cli/check.rs +73 -17
  15. package/rust/cli/init.rs +77 -0
  16. package/rust/cli/mod.rs +2 -1
  17. package/rust/cli/template.rs +56 -0
  18. package/rust/core/lexer/at.rs +21 -0
  19. package/rust/core/lexer/brace.rs +41 -0
  20. package/rust/core/lexer/bracket.rs +41 -0
  21. package/rust/core/lexer/colon.rs +21 -0
  22. package/rust/core/lexer/comment.rs +30 -0
  23. package/rust/core/lexer/dot.rs +21 -0
  24. package/rust/core/lexer/driver.rs +286 -0
  25. package/rust/core/lexer/equal.rs +32 -0
  26. package/rust/core/lexer/identifier.rs +38 -0
  27. package/rust/core/lexer/indent.rs +47 -0
  28. package/rust/core/lexer/mod.rs +14 -333
  29. package/rust/core/lexer/newline.rs +23 -0
  30. package/rust/core/lexer/number.rs +31 -0
  31. package/rust/core/lexer/quote.rs +61 -0
  32. package/rust/core/parser/dot.rs +48 -18
  33. package/rust/core/preprocessor/module.rs +1 -1
  34. package/rust/core/types/cli.rs +53 -31
  35. package/rust/core/types/config.rs +15 -0
  36. package/rust/core/types/mod.rs +2 -1
  37. package/rust/main.rs +30 -19
  38. package/rust/utils/config.rs +13 -0
  39. package/rust/utils/file.rs +35 -0
  40. package/rust/utils/mod.rs +4 -1
  41. package/rust/utils/watcher.rs +25 -0
  42. package/templates/minimal/.devalang +4 -0
  43. package/templates/minimal/src/index.deva +2 -0
  44. package/templates/welcome/.devalang +4 -0
  45. package/templates/welcome/README.md +185 -0
  46. package/templates/welcome/samples/kick-808.wav +0 -0
  47. package/templates/welcome/src/index.deva +13 -0
  48. package/templates/welcome/src/variables.deva +5 -0
  49. package/rust/cli/new.rs +0 -1
package/.devalang ADDED
@@ -0,0 +1,4 @@
1
+ [defaults]
2
+ entry = "./examples"
3
+ output = "./output"
4
+ watch = true
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "devalang"
3
- version = "0.0.1-alpha.1"
3
+ version = "0.0.1-alpha.2"
4
4
  authors = ["Devaloop <contact@devaloop.com>"]
5
5
  description = "Write music like code. Devalang is a domain-specific language (DSL) for sound designers and music hackers. Compose, automate, and control sound — in plain text."
6
6
  license = "MIT"
@@ -42,4 +42,5 @@ serde-wasm-bindgen = "0.4"
42
42
  nom_locate = "4.0.0"
43
43
  chrono = "0.4"
44
44
  crossterm = { version = "0.27", optional = true }
45
- indicatif = "0.17"
45
+ indicatif = "0.17"
46
+ inquire = "0.7.5"
package/README.md CHANGED
@@ -41,6 +41,7 @@ From studio sketches to live sets, Devalang gives you rhythmic control — with
41
41
  - 🧩 Module system for importing and exporting variables between files (`@import`, `@export`)
42
42
  - 📜 Structured AST generation for debugging and future compilation
43
43
  - 🔢 Basic data types: strings, numbers, booleans, maps, arrays
44
+ - 👁️ Watch mode for `build` and `check` commands
44
45
  - ⏱️ `bpm` assignment for setting tempo
45
46
  - 🧱 `bank` declaration to define the instrument set
46
47
  - 🔁 Looping system with fixed repetitions (`loop 4:`)
@@ -90,18 +91,40 @@ npm run rust:dev <command>
90
91
 
91
92
  For more examples, see [docs/COMMANDS.md](./docs/COMMANDS.md)
92
93
 
93
- Checking syntax only and output debug files
94
+ ### Initialize a new project
95
+
96
+ In the current directory
94
97
 
95
98
  ```bash
96
- devalang check --entry <entry-directory> --output <output-directory>
99
+ devalang init
97
100
  ```
98
101
 
99
- Building output file(s) (AST generation for the moment)
102
+ Or use optional arguments to specify a directory name and a template
100
103
 
101
104
  ```bash
102
- devalang build --entry <entry-directory> --output <output-directory>
105
+ devalang init --name <project-name> --template <template-name>
103
106
  ```
104
107
 
108
+ ### Checking syntax only and output debug files
109
+
110
+ ```bash
111
+ devalang check --entry <entry-directory> --output <output-directory> --watch
112
+ ```
113
+
114
+ ### Building output file(s) (AST generation for the moment)
115
+
116
+ ```bash
117
+ devalang build --entry <entry-directory> --output <output-directory> --watch
118
+ ```
119
+
120
+ ## ⚙️ Configuration
121
+
122
+ You can use a configuration file to set default values for various settings, making it easier to manage your Devalang project.
123
+
124
+ To do this, create a `.devalang` file in the root of your project directory.
125
+
126
+ See [docs/CONFIG.md](./docs/CONFIG.md) for more information.
127
+
105
128
  ## 📄 Syntax example
106
129
 
107
130
  For more examples, see [docs/SYNTAX.md](./docs/SYNTAX.md)
@@ -127,7 +150,7 @@ loop 5:
127
150
 
128
151
  let globalBpm = 120
129
152
  let globalBank = 808
130
- let kickDuration = 500
153
+ let kickDuration = 500
131
154
 
132
155
  @export { globalBpm, globalBank, kickDuration }
133
156
  ```
@@ -145,7 +168,8 @@ For more info, see [docs/ROADMAP.md](./docs/ROADMAP.md)
145
168
 
146
169
  - ⏳ Audio engine integration
147
170
  - ⏳ Other statements (e.g `if`, `@group`, ...)
148
- - ⏳ Watch mode for automatic rebuilds
171
+ - ⏳ Cross-platform support (Linux, macOS)
172
+ - ⏳ More built-in instruments (e.g. snare, hi-hat, etc.)
149
173
 
150
174
  ## 🛡️ License
151
175
 
@@ -0,0 +1,53 @@
1
+ <div align="center">
2
+ <img src="https://firebasestorage.googleapis.com/v0/b/devaloop-labs.firebasestorage.app/o/devalang-teal-logo.svg?alt=media&token=d2a5705a-1eba-4b49-88e6-895a761fb7f7" alt="Devalang Logo">
3
+ </div>
4
+
5
+ # Changelog
6
+
7
+ ## Version 0.0.1-alpha.2 (2024-06-26)
8
+
9
+ ### Commands
10
+
11
+ - Implemented `init` command to initialize a new Devalang project.
12
+ - Implemented `template` command to manage templates.
13
+ - Added `list` subcommand to list available templates.
14
+ - Added `info` subcommand to show information about a specific template.
15
+ - Implemented `watch` subcommand for the `build` and `check` command to watch for changes in files and automatically rebuild or check them.
16
+
17
+ ### Core Components
18
+
19
+ - Implemented Config manager to handle configuration files.
20
+ - Added support for `.devalang` configuration file as a TOML file.
21
+ - Added support for `--no-config` flag to disable configuration file usage.
22
+ - Implemented File System watcher to monitor file changes.
23
+ - Implemented Template manager to handle templates and their metadata.
24
+ - Refactored Lexer to support new syntax elements and directives.
25
+
26
+ ### Syntax
27
+
28
+ - Added support for built-in triggers for `.snare`, `.hihat`, `.clap`, `.tom`, `.crash`, `.ride`.
29
+ - Added support for custom triggers with `@load` using the syntax `.trigger-name`.
30
+
31
+ ## Version 0.0.1-alpha.1 (2024-06-25)
32
+
33
+ ### Syntax
34
+
35
+ - Added support for `@import` directive to import other Devalang files.
36
+ - Added support for `@export` directive to export variables and functions.
37
+ - Added support for `@load` directive to load external resources.
38
+ - Added support for `bpm` directive to set the beats per minute.
39
+ - Added support for `bank` directive to define a bank of sounds.
40
+ - Added support for `loop` directive to define loops in the code.
41
+
42
+ ### Commands
43
+
44
+ - Implemented `check` command to check the syntax of Devalang files.
45
+ - Implemented `build` command to build the Abstract Syntax Tree (AST) of Devalang files.
46
+
47
+ ### Core Components
48
+
49
+ - Implemented Lexer to tokenize Devalang source code.
50
+ - Implemented Parser to parse the tokens and build the AST.
51
+ - Implemented Preprocessor to handle directives and preprocess the source code.
52
+ - Implemented Debugger to debug Devalang code.
53
+ - Implemented Builder to build the final output from the AST.
package/docs/COMMANDS.md CHANGED
@@ -4,28 +4,51 @@
4
4
 
5
5
  # Devalang Commands Guide
6
6
 
7
+ ## Initialization
8
+
9
+ Initialize a new Devalang project (current folder)
10
+
11
+ ```bash
12
+ devalang init
13
+ ```
14
+
15
+ Initialize a new Devalang project (new folder)
16
+
17
+ ```bash
18
+ devalang init --name <project-name> --template <template-name>
19
+ ```
20
+
21
+ Available arguments:
22
+
23
+ - `--name`: The name of the project (cannot be empty)
24
+ - `--template`: The template to use for the project (default to `welcome`)
25
+
7
26
  ## Checking
8
27
 
9
28
  Checking syntax of .deva file(s)
10
29
 
11
30
  ```bash
12
- devalang check --entry ./examples --output ./output
31
+ devalang check --entry ./examples --output ./output --watch
13
32
  ```
14
33
 
15
34
  Available arguments :
16
35
 
17
- - `entry`: The input folder (default to `./src`)
18
- - `output`: The output folder (default to `./output`)
36
+ - `--no-config`: Whether to ignore the configuration file (default to `false`)
37
+ - `--entry`: The input folder (default to `./src`)
38
+ - `--output`: The output folder (default to `./output`)
39
+ - `--watch`: Whether to watch for changes and re-analyze (default to `false`)
19
40
 
20
41
  ## Building
21
42
 
22
43
  Building AST of .deva file(s)
23
44
 
24
45
  ```bash
25
- devalang build --entry ./examples --output ./output
46
+ devalang build --entry ./examples --output ./output --watch
26
47
  ```
27
48
 
28
49
  Available arguments :
29
50
 
30
- - `entry`: The input folder (default to `./src`)
31
- - `output`: The output folder (default to `./output`)
51
+ - `--no-config`: Whether to ignore the configuration file (default to `false`)
52
+ - `--entry`: The input folder (default to `./src`)
53
+ - `--output`: The output folder (default to `./output`)
54
+ - `--watch`: Whether to watch for changes and rebuild (default to `false`)
package/docs/CONFIG.md ADDED
@@ -0,0 +1,28 @@
1
+ <div align="center">
2
+ <img src="https://firebasestorage.googleapis.com/v0/b/devaloop-labs.firebasestorage.app/o/devalang-teal-logo.svg?alt=media&token=d2a5705a-1eba-4b49-88e6-895a761fb7f7" alt="Devalang Logo">
3
+ </div>
4
+
5
+ # Devalang Configuration File
6
+
7
+ Use a configuration file if you don't want to pass command-line arguments every time you run a command. The configuration file allows you to set default values for various settings, making it easier to manage your Devalang project.
8
+
9
+ ## Ignoring the Configuration File
10
+
11
+ If you prefer not to use a configuration file, you can ignore it by passing the `--no-config` flag when running Devalang commands. This will bypass any settings defined in the configuration file and use only the command-line arguments you provide.
12
+
13
+ ## Structure of the Configuration File
14
+
15
+ The configuration file is a TOML (Tom's Obvious, Minimal Language) file that contains key-value pairs to define various settings for your Devalang project. Below is a sample configuration file:
16
+
17
+ ```toml
18
+ [defaults]
19
+ entry = "./src"
20
+ output = "./output"
21
+ watch = true
22
+ ```
23
+
24
+ ### Available Settings
25
+
26
+ - `entry`: (String) The entry point for your Devalang project
27
+ - `output`: (String) The output directory for generated files
28
+ - `watch`: (Boolean) Whether to watch for changes in files and automatically rebuild or check them
package/docs/ROADMAP.md CHANGED
@@ -7,6 +7,7 @@
7
7
  Devalang is a work in progress. Here’s what we’re planning next:
8
8
 
9
9
  - ✅ **Basic syntax**: Implement the core syntax for Devalang, including data types and basic statements.
10
+ - ✅ **Watch mode**: Add a watch mode to automatically rebuild on file changes.
10
11
  - ✅ **Module system**: Add support for importing and exporting variables between files using `@import` and `@export`.
11
12
  - ✅ **AST generation**: Implement the Abstract Syntax Tree (AST) generation for debugging and future compilation.
12
13
  - ✅ **Basic data types**: Support strings, numbers, booleans, maps, and arrays.
@@ -21,7 +22,6 @@ Devalang is a work in progress. Here’s what we’re planning next:
21
22
  - ⏳ **WASM support**: Compile Devalang to WebAssembly for use in web applications.
22
23
  - ⏳ **Other statements**: Implement `if`, `else`, and other control structures.
23
24
  - ⏳ **Pattern and group statements**: Add support for `@pattern` and `@group` to organize code.
24
- - ⏳ **Watch mode**: Add a watch mode to automatically rebuild on file changes.
25
25
  - ⏳ **Functions**: Add support for defining and calling functions.
26
26
  - ⏳ **Audio engine**: Integrate the audio engine for sound playback.
27
27
  - ⏳ **Testing**: Expand test coverage for all features.
package/docs/TODO.md CHANGED
@@ -8,16 +8,19 @@ This is a list of tasks and features to be implemented in Devalang. Note that th
8
8
 
9
9
  ## Commands
10
10
 
11
- - [ ] New project
12
- - [ ] Template
13
- - [ ] Implement template list
14
- - [ ] Implement template info
11
+ - [x] Init project
12
+ - [x] Implement init command
13
+ - [x] Implement template selector
14
+ - [ ] Implement project name validation
15
+ - [x] Template
16
+ - [x] Implement template list
17
+ - [x] Implement template info
15
18
  - [x] Checking
16
- - [ ] Implement watch mode
19
+ - [x] Implement watch mode
17
20
  - [ ] Implement debug mode
18
21
  - [ ] Implement compilation mode
19
22
  - [x] Building
20
- - [ ] Implement watch mode
23
+ - [x] Implement watch mode
21
24
  - [ ] Implement debug mode
22
25
  - [ ] Implement compilation mode
23
26
  - [ ] Implement compression mode
@@ -28,6 +31,7 @@ This is a list of tasks and features to be implemented in Devalang. Note that th
28
31
  ## Core components
29
32
 
30
33
  - [ ] Audio Engine
34
+ - [x] Configuration
31
35
  - [x] Lexer
32
36
  - [x] Parser
33
37
  - [x] Preprocessor
@@ -36,7 +40,7 @@ This is a list of tasks and features to be implemented in Devalang. Note that th
36
40
 
37
41
  ## Syntax elements
38
42
 
39
- - [ ] #
43
+ - [x] #
40
44
  - [x] @import
41
45
  - [x] @export
42
46
  - [ ] @group
@@ -52,15 +56,39 @@ This is a list of tasks and features to be implemented in Devalang. Note that th
52
56
  - [ ] else
53
57
  - [ ] else if
54
58
 
55
- ## Built-in triggers
59
+ ## Triggers
60
+
61
+ ### One-shot triggers
56
62
 
57
63
  - [x] .kick
58
- - [ ] .snare
59
- - [ ] .hihat
60
- - [ ] .tom
61
- - [ ] .clap
62
- - [ ] .crash
63
- - [ ] .ride
64
- - [ ] .synth
64
+ - [x] .snare
65
+ - [x] .hihat
66
+ - [x] .tom
67
+ - [x] .clap
68
+ - [x] .crash
69
+ - [x] .ride
70
+
71
+ ### Instrument triggers
72
+
73
+ - [ ] .guitar
74
+ - [ ] .piano
75
+ - [ ] .flute
76
+ - [ ] .violin
77
+
78
+ ### Synth & effects triggers
79
+
80
+ - [ ] .lead
65
81
  - [ ] .bass
66
82
  - [ ] .pad
83
+ - [ ] .arp
84
+ - [ ] .fx
85
+ - [ ] .vocal
86
+
87
+ ## Other TODOs
88
+
89
+ - [ ] Possibility to overload built-in triggers
90
+ - [ ] Implement a more robust error handling system
91
+ - [ ] Replace eprintln & println with `log_message` function
92
+ - [ ] Implement a more comprehensive logging system
93
+ - [ ] Add unit tests for all core components
94
+ - [ ] Comment all core components
@@ -6,4 +6,4 @@ bpm tempo
6
6
  bank default_bank
7
7
 
8
8
  loop loopCount:
9
- .sample duration params
9
+ .sample duration params
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@devaloop/devalang",
3
3
  "private": false,
4
- "version": "0.0.1-alpha.1",
4
+ "version": "0.0.1-alpha.2",
5
5
  "description": "Write music like code. Devalang is a domain-specific language (DSL) for sound designers and music hackers. Compose, automate, and control sound — in plain text.",
6
6
  "main": "out-tsc/index.js",
7
7
  "bin": {
@@ -40,4 +40,4 @@
40
40
  "dependencies": {
41
41
  "@types/node": "^24.0.3"
42
42
  }
43
- }
43
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.0.1-alpha.1",
2
+ "version": "0.0.1-alpha.2",
3
3
  "channel": "alpha",
4
- "lastCommit": "",
5
- "build": 1
4
+ "lastCommit": "6f92a4421db96f45c6accd6607e09876c0735531",
5
+ "build": 2
6
6
  }
package/rust/cli/build.rs CHANGED
@@ -5,24 +5,77 @@ use crate::{
5
5
  builder::{ build_ast, write_ast_to_file },
6
6
  debugger::Debugger,
7
7
  preprocessor::module::load_all_modules,
8
+ types::config::DevalangConfig,
8
9
  },
9
10
  runner::executer::execute_statements,
10
- utils::{ loader::with_spinner, logger::log_message, path::{ find_entry_file, normalize_path } },
11
+ utils::{
12
+ loader::with_spinner,
13
+ logger::log_message,
14
+ path::{ find_entry_file, normalize_path },
15
+ watcher::watch_directory,
16
+ },
11
17
  };
12
18
 
13
- pub fn handle_build_command(entry: String, output: String) {
14
- let entry_file = find_entry_file(&entry).unwrap_or_else(|| {
15
- eprintln!("❌ index.deva not found in directory: {}", entry);
19
+ pub fn handle_build_command(
20
+ config: Option<DevalangConfig>,
21
+ entry: Option<String>,
22
+ output: Option<String>,
23
+ watch: bool
24
+ ) {
25
+ let fetched_entry = if entry.is_none() {
26
+ config
27
+ .as_ref()
28
+ .and_then(|c| c.defaults.entry.clone())
29
+ .unwrap_or_else(|| "".to_string())
30
+ } else {
31
+ entry.clone().unwrap_or_else(|| "".to_string())
32
+ };
33
+
34
+ let fetched_output = if output.is_none() {
35
+ config
36
+ .as_ref()
37
+ .and_then(|c| c.defaults.output.clone())
38
+ .unwrap_or_else(|| "".to_string())
39
+ } else {
40
+ output.clone().unwrap_or_else(|| "".to_string())
41
+ };
42
+
43
+ let fetched_watch = if watch {
44
+ watch
45
+ } else {
46
+ config
47
+ .as_ref()
48
+ .and_then(|c| c.defaults.watch)
49
+ .unwrap_or(false)
50
+ };
51
+
52
+ let entry_file = find_entry_file(&fetched_entry).unwrap_or_else(|| {
53
+ eprintln!("❌ index.deva not found in directory: {}", fetched_entry);
16
54
  std::process::exit(1);
17
55
  });
18
56
 
57
+ if watch == true {
58
+ log_message("Watch mode enabled, waiting for file changes...", "INFO");
59
+
60
+ begin_build(entry_file.clone(), fetched_output.clone(), watch);
61
+
62
+ watch_directory(entry_file.clone(), move || {
63
+ log_message("File change detected, rebuilding...", "INFO");
64
+ begin_build(entry_file.clone(), fetched_output.clone(), watch);
65
+ }).unwrap();
66
+ } else {
67
+ begin_build(entry_file.clone(), fetched_output.clone(), watch);
68
+ }
69
+ }
70
+
71
+ fn begin_build(entry: String, output: String, watch: bool) {
19
72
  let spinner = with_spinner("Building...", || {
20
73
  thread::sleep(Duration::from_millis(800));
21
74
  });
22
75
 
23
76
  let duration = std::time::Instant::now();
24
77
 
25
- let normalized_entry_file = normalize_path(&entry_file);
78
+ let normalized_entry_file = normalize_path(&entry);
26
79
  let normalized_output_dir = normalize_path(&output);
27
80
 
28
81
  let global_store = load_all_modules(&normalized_entry_file);
package/rust/cli/check.rs CHANGED
@@ -5,33 +5,89 @@ use crate::{
5
5
  debugger::Debugger,
6
6
  preprocessor::module::load_all_modules,
7
7
  types::{
8
- statement::{
9
- Statement,
10
- StatementIterator,
11
- StatementKind,
12
- StatementResolved,
13
- StatementResolvedValue,
14
- },
15
- variable::VariableValue,
8
+ config::{ DevalangConfig },
9
+ statement::{ StatementKind, StatementResolved, StatementResolvedValue },
16
10
  },
17
11
  },
18
12
  runner::executer::execute_statements,
19
- utils::{ loader::with_spinner, logger::log_message, path::{ find_entry_file, normalize_path } },
13
+ utils::{
14
+ loader::with_spinner,
15
+ logger::log_message,
16
+ path::{ find_entry_file, normalize_path },
17
+ watcher::watch_directory,
18
+ },
20
19
  };
21
20
 
22
- pub fn handle_check_command(entry: String, output: String) -> () {
23
- let entry_file = find_entry_file(&entry).unwrap_or_else(|| {
24
- eprintln!("❌ index.deva not found in directory: {}", entry);
21
+ pub fn handle_check_command(
22
+ config: Option<DevalangConfig>,
23
+ entry: Option<String>,
24
+ output: Option<String>,
25
+ watch: bool
26
+ ) -> () {
27
+ let fetched_entry = if entry.is_none() {
28
+ config
29
+ .as_ref()
30
+ .and_then(|c| c.defaults.entry.clone())
31
+ .unwrap_or_else(|| "".to_string())
32
+ } else {
33
+ entry.clone().unwrap_or_else(|| "".to_string())
34
+ };
35
+
36
+ let fetched_output = if output.is_none() {
37
+ config
38
+ .as_ref()
39
+ .and_then(|c| c.defaults.output.clone())
40
+ .unwrap_or_else(|| "".to_string())
41
+ } else {
42
+ output.clone().unwrap_or_else(|| "".to_string())
43
+ };
44
+
45
+ let fetched_watch = if watch {
46
+ watch
47
+ } else {
48
+ config
49
+ .as_ref()
50
+ .and_then(|c| c.defaults.watch)
51
+ .unwrap_or(false)
52
+ };
53
+
54
+ if fetched_entry.is_empty() {
55
+ eprintln!("❌ Entry path is not specified. Please provide a valid entry path.");
56
+ std::process::exit(1);
57
+ }
58
+
59
+ if fetched_output.is_empty() {
60
+ eprintln!("❌ Output directory is not specified. Please provide a valid output directory.");
61
+ std::process::exit(1);
62
+ }
63
+
64
+ let entry_file = find_entry_file(&fetched_entry).unwrap_or_else(|| {
65
+ eprintln!("❌ index.deva not found in directory: {}", fetched_entry);
25
66
  std::process::exit(1);
26
67
  });
27
68
 
69
+ if fetched_watch.clone() == true {
70
+ log_message("Watch mode enabled, waiting for file changes...", "INFO");
71
+
72
+ begin_check(entry_file.clone(), fetched_output.clone(), fetched_watch.clone());
73
+
74
+ watch_directory(entry_file.clone(), move || {
75
+ log_message("File change detected, rebuilding...", "INFO");
76
+ begin_check(entry_file.clone(), fetched_output.clone(), fetched_watch.clone());
77
+ }).unwrap();
78
+ } else {
79
+ begin_check(entry_file.clone(), fetched_output.clone(), fetched_watch.clone());
80
+ }
81
+ }
82
+
83
+ fn begin_check(entry: String, output: String, watch: bool) {
28
84
  let spinner = with_spinner("Checking...", || {
29
85
  thread::sleep(Duration::from_millis(800));
30
86
  });
31
87
 
32
88
  let duration = std::time::Instant::now();
33
89
 
34
- let normalized_entry_file = normalize_path(&entry_file);
90
+ let normalized_entry_file = normalize_path(&entry);
35
91
  let normalized_output_dir = normalize_path(&output);
36
92
 
37
93
  let global_store = load_all_modules(&normalized_entry_file);
@@ -45,9 +101,9 @@ pub fn handle_check_command(entry: String, output: String) -> () {
45
101
  let debug_dir = format!("{}/debug/", normalized_output_dir.clone());
46
102
  debugger.write_files(debug_dir.as_str(), resolved_statements.clone());
47
103
 
48
- let has_errors = resolved_statements.iter().any(|stmt| {
49
- match_error_recursively_resolved(&stmt.clone())
50
- });
104
+ let has_errors = resolved_statements
105
+ .iter()
106
+ .any(|stmt| { match_error_recursively_resolved(&stmt.clone()) });
51
107
 
52
108
  if has_errors {
53
109
  let warning_message = format!(
@@ -109,7 +165,7 @@ fn match_error_recursively_resolved_value(value: &StatementResolvedValue) -> boo
109
165
  }
110
166
  }
111
167
  }
112
-
168
+
113
169
  StatementResolvedValue::Array(array) => {
114
170
  for item in array {
115
171
  if match_error_recursively_resolved(item) {
@@ -0,0 +1,77 @@
1
+ use std::{ fs, path::Path };
2
+ use include_dir::{ include_dir, Dir };
3
+ use inquire::{ Select, Confirm };
4
+
5
+ use crate::{ cli::template::{ get_available_templates }, utils::file::copy_dir_recursive };
6
+
7
+ static TEMPLATES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates");
8
+
9
+ pub fn handle_init_command(name: Option<String>, template: Option<String>) {
10
+ let current_dir = std::env::current_dir().unwrap();
11
+ let project_name = name
12
+ .clone()
13
+ .unwrap_or_else(|| {
14
+ current_dir.file_name().unwrap_or_default().to_string_lossy().to_string()
15
+ });
16
+
17
+ // Select a template if not provided
18
+ let selected_template = template.unwrap_or_else(|| {
19
+ Select::new("Select a template for your project:", get_available_templates())
20
+ .prompt()
21
+ .unwrap_or_else(|_| {
22
+ eprintln!("No template selected. Exiting...");
23
+ std::process::exit(1);
24
+ })
25
+ });
26
+
27
+ if selected_template.is_empty() {
28
+ eprintln!("Template cannot be empty.");
29
+ std::process::exit(1);
30
+ }
31
+
32
+ if name.is_none() {
33
+ // Case of initialization in the current directory
34
+ if fs::read_dir(&current_dir).unwrap().next().is_some() {
35
+ let confirm = Confirm::new(
36
+ "The current directory is not empty. Do you want to continue?"
37
+ )
38
+ .with_default(false)
39
+ .prompt()
40
+ .unwrap_or(false);
41
+
42
+ if !confirm {
43
+ eprintln!("Operation cancelled by the user.");
44
+ std::process::exit(0);
45
+ }
46
+ }
47
+
48
+ scaffold_project_current_dir(current_dir.as_path(), selected_template);
49
+ println!(
50
+ "✅ Initialized '{}' project in current directory: {}",
51
+ project_name,
52
+ current_dir.display()
53
+ );
54
+ } else {
55
+ // Case of initialization in a new directory
56
+ let target_path = current_dir.join(&project_name);
57
+
58
+ if target_path.exists() {
59
+ eprintln!("❌ A folder named '{}' already exists.", project_name);
60
+ std::process::exit(1);
61
+ }
62
+
63
+ fs::create_dir_all(&target_path).expect("Error creating project directory");
64
+
65
+ scaffold_project_current_dir(&target_path, selected_template);
66
+ println!("✅ Initialized '{}' project in: {}", project_name, target_path.display());
67
+ }
68
+ }
69
+
70
+ fn scaffold_project_current_dir(path: &Path, template: String) {
71
+ let template_dir = TEMPLATES_DIR.get_dir(&template).unwrap_or_else(|| {
72
+ eprintln!("❌ The template '{}' doesn't exist.", template);
73
+ std::process::exit(1);
74
+ });
75
+
76
+ copy_dir_recursive(template_dir, path, &template_dir.path());
77
+ }
package/rust/cli/mod.rs CHANGED
@@ -1,3 +1,4 @@
1
1
  pub mod check;
2
- pub mod new;
2
+ pub mod init;
3
3
  pub mod build;
4
+ pub mod template;