diff --git a/assets/project-templates/place/.luaurc b/assets/project-templates/place/.luaurc new file mode 100644 index 00000000..0c47f4bc --- /dev/null +++ b/assets/project-templates/place/.luaurc @@ -0,0 +1,7 @@ +{ + "languageMode": "strict", + "lint": { "*": true, "LocalUnused": false }, + "lintErrors": false, + "typeErrors": true, + "globals": [] +} \ No newline at end of file diff --git a/assets/project-templates/place/.vscode/extensions.json b/assets/project-templates/place/.vscode/extensions.json new file mode 100644 index 00000000..b1f73466 --- /dev/null +++ b/assets/project-templates/place/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "evaera.vscode-rojo", + "johnnymorganz.stylua", + "johnnymorganz.luau-lsp" + ] +} \ No newline at end of file diff --git a/assets/project-templates/place/.vscode/settings.json b/assets/project-templates/place/.vscode/settings.json new file mode 100644 index 00000000..95aa6113 --- /dev/null +++ b/assets/project-templates/place/.vscode/settings.json @@ -0,0 +1,32 @@ +{ + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.rulers": [ + 80 + ], + "files.eol": "\n", + "files.encoding": "utf8", + "files.autoGuessEncoding": false, + + "luau-lsp.plugin.maximumRequestBodySize": "500mb", + "luau-lsp.plugin.enabled": true, + "luau-lsp.completion.autocompleteEnd": true, + "luau-lsp.completion.imports.enabled": true, + "luau-lsp.completion.imports.separateGroupsWithLine": true, + "luau-lsp.completion.showPropertiesOnMethodCall": true, + "luau-lsp.hover.multilineFunctionDefinitions": true, + "luau-lsp.hover.showTableKinds": true, + "luau-lsp.inlayHints.makeInsertable": false, + "luau-lsp.inlayHints.hideHintsForMatchingParameterNames": false, + "luau-lsp.sourcemap.autogenerate": true, + "luau-lsp.sourcemap.includeNonScripts": true, + "luau-lsp.diagnostics.strictDatamodelTypes": true, + "luau-lsp.completion.imports.requireStyle": "alwaysAbsolute", + "luau-lsp.fflags.enableNewSolver": false, + + "[luau]": { + "editor.defaultFormatter": "JohnnyMorganz.stylua" + }, + "stylua.targetReleaseVersion": "latest", + "git.ignoreLimitWarning": true, +} diff --git a/assets/project-templates/place/README.md b/assets/project-templates/place/README.md index 7908cf59..0c3f6180 100644 --- a/assets/project-templates/place/README.md +++ b/assets/project-templates/place/README.md @@ -1,17 +1,10 @@ # {project_name} -Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}. -## Getting Started -To build the place from scratch, use: +Generated by [Atlas](https://github.com/UserGeneratedLLC/rojo). -```bash -rojo build -o "{project_name}.rbxlx" -``` - -Next, open `{project_name}.rbxlx` in Roblox Studio and start the Rojo server: +## Commands ```bash rojo serve +rojo syncback ``` - -For more help, check out [the Rojo documentation](https://rojo.space/docs). \ No newline at end of file diff --git a/assets/project-templates/place/default.project.json5 b/assets/project-templates/place/default.project.json5 index b6e6410a..ad33796d 100644 --- a/assets/project-templates/place/default.project.json5 +++ b/assets/project-templates/place/default.project.json5 @@ -1,72 +1,9 @@ { - "name": "{project_name}", - "tree": { - "$className": "DataModel", - - "ReplicatedStorage": { - "Shared": { - "$path": "src/shared" - } - }, - - "ServerScriptService": { - "Server": { - "$path": "src/server" - } - }, - - "StarterPlayer": { - "StarterPlayerScripts": { - "Client": { - "$path": "src/client" - } - } - }, - - "Workspace": { - "$properties": { - "FilteringEnabled": true - }, - "Baseplate": { - "$className": "Part", - "$properties": { - "Anchored": true, - "Color": [ - 0.38823, - 0.37254, - 0.38823 - ], - "Locked": true, - "Position": [ - 0, - -10, - 0 - ], - "Size": [ - 512, - 20, - 512 - ] - } - } - }, - "Lighting": { - "$properties": { - "Ambient": [ - 0, - 0, - 0 - ], - "Brightness": 2, - "GlobalShadows": true, - "Outlines": false, - "Technology": "Voxel" - } - }, - "SoundService": { - "$properties": { - "RespectFilteringEnabled": true - } - } - } -} \ No newline at end of file + name: "{project_name}", + servePlaceIds: [{place_id}], + syncScriptsOnly: true, + tree: { + $className: "DataModel", + $path: "src" + } +} diff --git a/assets/project-templates/place/gitignore.txt b/assets/project-templates/place/gitignore.txt index 8a0a285b..0ae5edc7 100644 --- a/assets/project-templates/place/gitignore.txt +++ b/assets/project-templates/place/gitignore.txt @@ -1,8 +1,10 @@ # Project place file -/{project_name}.rbxlx +/*.rbxlx +/*.rbxl # Roblox Studio lock files /*.rbxlx.lock /*.rbxl.lock -sourcemap.json +# Sourcemap +/sourcemap.json diff --git a/assets/project-templates/place/src/.gitkeep b/assets/project-templates/place/src/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/assets/project-templates/place/src/client/init.client.luau b/assets/project-templates/place/src/client/init.client.luau deleted file mode 100644 index 5b0486bf..00000000 --- a/assets/project-templates/place/src/client/init.client.luau +++ /dev/null @@ -1 +0,0 @@ -print("Hello world, from client!") diff --git a/assets/project-templates/place/src/server/init.server.luau b/assets/project-templates/place/src/server/init.server.luau deleted file mode 100644 index ec610e9c..00000000 --- a/assets/project-templates/place/src/server/init.server.luau +++ /dev/null @@ -1 +0,0 @@ -print("Hello world, from server!") diff --git a/assets/project-templates/place/src/shared/Hello.luau b/assets/project-templates/place/src/shared/Hello.luau deleted file mode 100644 index 56ec1f24..00000000 --- a/assets/project-templates/place/src/shared/Hello.luau +++ /dev/null @@ -1,3 +0,0 @@ -return function() - print("Hello, world!") -end diff --git a/assets/project-templates/place/stylua.toml b/assets/project-templates/place/stylua.toml new file mode 100644 index 00000000..5e4b2009 --- /dev/null +++ b/assets/project-templates/place/stylua.toml @@ -0,0 +1,12 @@ +syntax = "Luau" +column_width = 80 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" +call_parentheses = "Always" +collapse_simple_statement = "FunctionOnly" +space_after_function_names = "Never" + +[sort_requires] +enabled = true diff --git a/src/cli/init.rs b/src/cli/init.rs index eb323310..8e9ca0ec 100644 --- a/src/cli/init.rs +++ b/src/cli/init.rs @@ -28,7 +28,7 @@ static TEMPLATE_BINCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/templ #[derive(Debug, Parser)] pub struct InitCommand { /// Path to the place to create the project. Defaults to the current directory. - #[clap(default_value = "")] + #[clap(long, default_value = ".")] pub path: PathBuf, /// The kind of project to create, 'place', 'plugin', or 'model'. @@ -38,6 +38,13 @@ pub struct InitCommand { /// Skips the initialization of a git repository. #[clap(long)] pub skip_git: bool, + + /// Place ID to use for servePlaceIds. + pub placeid: Option, + + /// Skip cloning cursor rules into .cursor directory. + #[clap(long)] + pub skip_rules: bool, } impl InitCommand { @@ -45,6 +52,18 @@ impl InitCommand { let template = self.kind.template(); let base_path = resolve_path(&self.path); + + // Check if directory exists and is non-empty + if base_path.exists() { + let is_empty = base_path.read_dir()?.next().is_none(); + if !is_empty { + bail!( + "Directory '{}' is not empty. Please use an empty directory.", + base_path.display() + ); + } + } + fs::create_dir_all(&base_path)?; let canonical = fs::canonicalize(&base_path)?; @@ -55,6 +74,7 @@ impl InitCommand { let project_params = ProjectParams { name: project_name.to_owned(), + place_id: self.placeid, }; println!( @@ -95,7 +115,7 @@ impl InitCommand { } } - if !self.skip_git && should_git_init(&base_path) { + let did_git_init = if !self.skip_git && should_git_init(&base_path) { log::debug!("Initializing Git repository..."); let status = Command::new("git") @@ -106,6 +126,76 @@ impl InitCommand { if !status.success() { bail!("git init failed: status code {:?}", status.code()); } + true + } else { + !self.skip_git + }; + + if !self.skip_rules { + if did_git_init { + log::debug!("Adding cursor rules as submodule..."); + + let result = Command::new("git") + .args([ + "submodule", + "add", + "https://github.com/jrmelsha/cursor-rules.git", + ".cursor", + ]) + .current_dir(&base_path) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status(); + + match result { + Ok(status) if status.success() => { + println!("Added cursor rules as submodule."); + } + _ => { + log::debug!("Failed to add cursor rules submodule, skipping."); + } + } + } else { + log::debug!("Cloning cursor rules (no git repo)..."); + + let result = Command::new("git") + .args([ + "clone", + "https://github.com/jrmelsha/cursor-rules.git", + ".cursor", + ]) + .current_dir(&base_path) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status(); + + match result { + Ok(status) if status.success() => { + println!("Cloned cursor rules successfully."); + } + _ => { + log::debug!("Failed to clone cursor rules, skipping."); + } + } + } + } + + if did_git_init { + log::debug!("Committing initial project..."); + + let _ = Command::new("git") + .args(["add", "."]) + .current_dir(&base_path) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status(); + + let _ = Command::new("git") + .args(["commit", "-m", "Initial commit"]) + .current_dir(&base_path) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status(); } println!("Created project successfully."); @@ -172,14 +262,21 @@ impl FromStr for InitKind { /// Contains parameters used in templates to create a project. struct ProjectParams { name: String, + place_id: Option, } impl ProjectParams { /// Render a template by replacing variables with project parameters. fn render_template(&self, template: &str) -> String { + let place_id_str = self + .place_id + .map(|id| id.to_string()) + .unwrap_or_else(|| "0".to_string()); + template .replace("{project_name}", &self.name) .replace("{rojo_version}", env!("CARGO_PKG_VERSION")) + .replace("{place_id}", &place_id_str) } }