mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-23 22:25:26 +00:00
Add brand new rojo init command
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
## Unreleased Changes for 0.6.x
|
## Unreleased Changes for 0.6.x
|
||||||
* Added `--watch` argument to `rojo build`. ([#284](https://github.com/rojo-rbx/rojo/pull/284))
|
* Added `--watch` argument to `rojo build`. ([#284](https://github.com/rojo-rbx/rojo/pull/284))
|
||||||
* Added dark theme support to plugin. ([#241](https://github.com/rojo-rbx/rojo/issues/241))
|
* Added dark theme support to plugin. ([#241](https://github.com/rojo-rbx/rojo/issues/241))
|
||||||
|
* Added a revamped `rojo init` command, which will now create more complete projects.
|
||||||
* Added the `rojo doc` command, which opens Rojo's documentation in your browser.
|
* Added the `rojo doc` command, which opens Rojo's documentation in your browser.
|
||||||
* Simplified filesystem access code dramatically.
|
* Simplified filesystem access code dramatically.
|
||||||
* Improved error reporting and logging across the board.
|
* Improved error reporting and logging across the board.
|
||||||
|
|||||||
5
assets/default-model.lua
Normal file
5
assets/default-model.lua
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
return {
|
||||||
|
hello = function()
|
||||||
|
print("Hello world, from {project_name}!")
|
||||||
|
end,
|
||||||
|
}
|
||||||
6
assets/model.project.json
Normal file
6
assets/model.project.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "{project_name}",
|
||||||
|
"tree": {
|
||||||
|
"$path": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,39 +1,36 @@
|
|||||||
{
|
{
|
||||||
"name": "[placeholder]",
|
"name": "{project_name}",
|
||||||
"tree": {
|
"tree": {
|
||||||
"$className": "DataModel",
|
"$className": "DataModel",
|
||||||
"HttpService": {
|
|
||||||
"$className": "HttpService",
|
|
||||||
"$properties": {
|
|
||||||
"HttpEnabled": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Lighting": {
|
|
||||||
"$className": "Lighting",
|
|
||||||
"$properties": {
|
|
||||||
"Ambient": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"Brightness": 2,
|
|
||||||
"GlobalShadows": true,
|
|
||||||
"Outlines": false,
|
|
||||||
"Technology": "Voxel"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ReplicatedStorage": {
|
"ReplicatedStorage": {
|
||||||
"$className": "ReplicatedStorage",
|
"$className": "ReplicatedStorage",
|
||||||
"Source": {
|
|
||||||
"$path": "src"
|
"Common": {
|
||||||
|
"$path": "src/common"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SoundService": {
|
|
||||||
"$className": "SoundService",
|
"ServerScriptService": {
|
||||||
"$properties": {
|
"$className": "ServerScriptService",
|
||||||
"RespectFilteringEnabled": true
|
|
||||||
|
"Server": {
|
||||||
|
"$path": "src/server"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"StarterPlayer": {
|
||||||
|
"$className": "StarterPlayer",
|
||||||
|
|
||||||
|
"StarterPlayerScripts": {
|
||||||
|
"$className": "StarterPlayerScripts",
|
||||||
|
|
||||||
|
"Client": {
|
||||||
|
"$path": "src/client"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"Workspace": {
|
"Workspace": {
|
||||||
"$className": "Workspace",
|
"$className": "Workspace",
|
||||||
"$properties": {
|
"$properties": {
|
||||||
@@ -61,6 +58,32 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Lighting": {
|
||||||
|
"$className": "Lighting",
|
||||||
|
"$properties": {
|
||||||
|
"Ambient": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"Brightness": 2,
|
||||||
|
"GlobalShadows": true,
|
||||||
|
"Outlines": false,
|
||||||
|
"Technology": "Voxel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SoundService": {
|
||||||
|
"$className": "SoundService",
|
||||||
|
"$properties": {
|
||||||
|
"RespectFilteringEnabled": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"HttpService": {
|
||||||
|
"$className": "HttpService",
|
||||||
|
"$properties": {
|
||||||
|
"HttpEnabled": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
147
src/cli/init.rs
147
src/cli/init.rs
@@ -1,17 +1,156 @@
|
|||||||
|
use std::{
|
||||||
|
fs::{self, OpenOptions},
|
||||||
|
io::{self, Write},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
use snafu::Snafu;
|
use snafu::Snafu;
|
||||||
|
|
||||||
use crate::cli::InitCommand;
|
use crate::cli::{InitCommand, InitKind};
|
||||||
|
|
||||||
|
static DEFAULT_PLACE_PROJECT: &str = include_str!("../../assets/place.project.json");
|
||||||
|
|
||||||
|
static DEFAULT_MODEL_PROJECT: &str = include_str!("../../assets/model.project.json");
|
||||||
|
static DEFAULT_MODEL_INIT: &str = include_str!("../../assets/default-model.lua");
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
pub struct InitError(Error);
|
pub struct InitError(Error);
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
enum Error {}
|
enum Error {
|
||||||
|
#[snafu(display("A project file named default.project.json already exists in this folder"))]
|
||||||
|
AlreadyExists,
|
||||||
|
|
||||||
|
#[snafu(display("I/O error"))]
|
||||||
|
Io { source: io::Error },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(source: io::Error) -> Self {
|
||||||
|
Self::Io { source }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(options: InitCommand) -> Result<(), InitError> {
|
pub fn init(options: InitCommand) -> Result<(), InitError> {
|
||||||
Ok(init_inner(options)?)
|
Ok(init_inner(options)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_inner(_options: InitCommand) -> Result<(), Error> {
|
fn init_inner(options: InitCommand) -> Result<(), Error> {
|
||||||
unimplemented!("init command");
|
let base_path = options.absolute_path();
|
||||||
|
let canonical = fs::canonicalize(&base_path)?;
|
||||||
|
let project_name = canonical
|
||||||
|
.file_name()
|
||||||
|
.and_then(|name| name.to_str())
|
||||||
|
.unwrap_or("new-project");
|
||||||
|
|
||||||
|
let project_params = ProjectParams {
|
||||||
|
name: project_name.to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match options.kind {
|
||||||
|
InitKind::Place => init_place(&base_path, project_params),
|
||||||
|
InitKind::Model => init_model(&base_path, project_params),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), Error> {
|
||||||
|
let project_file = project_params.render_template(DEFAULT_PLACE_PROJECT);
|
||||||
|
try_create_project(base_path, &project_file)?;
|
||||||
|
|
||||||
|
let src = base_path.join("src");
|
||||||
|
fs::create_dir_all(&src)?;
|
||||||
|
|
||||||
|
let src_common = src.join("common");
|
||||||
|
fs::create_dir_all(src.join(&src_common))?;
|
||||||
|
|
||||||
|
let src_server = src.join("server");
|
||||||
|
fs::create_dir_all(src.join(&src_server))?;
|
||||||
|
|
||||||
|
let src_client = src.join("client");
|
||||||
|
fs::create_dir_all(src.join(&src_client))?;
|
||||||
|
|
||||||
|
write_if_not_exists(
|
||||||
|
&src_common.join("Hello.lua"),
|
||||||
|
"return function()\n\tprint(\"Hello, world!\")\nend",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_if_not_exists(
|
||||||
|
&src_server.join("init.server.lua"),
|
||||||
|
"print(\"Hello world, from server!\")",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_if_not_exists(
|
||||||
|
&src_client.join("init.client.lua"),
|
||||||
|
"print(\"Hello world, from client!\")",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_model(base_path: &Path, project_params: ProjectParams) -> Result<(), Error> {
|
||||||
|
let project_file = project_params.render_template(DEFAULT_MODEL_PROJECT);
|
||||||
|
try_create_project(base_path, &project_file)?;
|
||||||
|
|
||||||
|
let src = base_path.join("src");
|
||||||
|
fs::create_dir_all(&src)?;
|
||||||
|
|
||||||
|
let init = project_params.render_template(DEFAULT_MODEL_INIT);
|
||||||
|
write_if_not_exists(&src.join("init.lua"), &init)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contains parameters used in templates to create a project.
|
||||||
|
struct ProjectParams {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProjectParams {
|
||||||
|
/// Render a template by replacing variables with project parameters.
|
||||||
|
fn render_template(&self, template: &str) -> String {
|
||||||
|
template.replace("{project_name}", &self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a file if it does not exist yet, otherwise, leave it alone.
|
||||||
|
fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), Error> {
|
||||||
|
let file_res = OpenOptions::new().write(true).create_new(true).open(path);
|
||||||
|
|
||||||
|
let mut file = match file_res {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
return match err.kind() {
|
||||||
|
io::ErrorKind::AlreadyExists => return Ok(()),
|
||||||
|
_ => Err(err.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
file.write_all(contents.as_bytes())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to create a project file and fail if it already exists.
|
||||||
|
fn try_create_project(base_path: &Path, contents: &str) -> Result<(), Error> {
|
||||||
|
let project_path = base_path.join("default.project.json");
|
||||||
|
|
||||||
|
let file_res = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(project_path);
|
||||||
|
|
||||||
|
let mut file = match file_res {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
return match err.kind() {
|
||||||
|
io::ErrorKind::AlreadyExists => Err(Error::AlreadyExists),
|
||||||
|
_ => Err(err.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
file.write_all(contents.as_bytes())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user