mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 12:45:05 +00:00
Refactor init command (#1117)
This commit is contained in:
@@ -2,4 +2,4 @@ return {
|
|||||||
hello = function()
|
hello = function()
|
||||||
print("Hello world, from {project_name}!")
|
print("Hello world, from {project_name}!")
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
print("Hello world, from client!")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
print("Hello world, from server!")
|
||||||
3
assets/project-templates/place/src/shared/Hello.luau
Normal file
3
assets/project-templates/place/src/shared/Hello.luau
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
return function()
|
||||||
|
print("Hello, world!")
|
||||||
|
end
|
||||||
1
assets/project-templates/plugin/src/init.server.luau
Normal file
1
assets/project-templates/plugin/src/init.server.luau
Normal file
@@ -0,0 +1 @@
|
|||||||
|
print("Hello world, from plugin!")
|
||||||
12
build.rs
12
build.rs
@@ -47,6 +47,7 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
|
|
||||||
let root_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
let root_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||||
let plugin_dir = root_dir.join("plugin");
|
let plugin_dir = root_dir.join("plugin");
|
||||||
|
let templates_dir = root_dir.join("assets").join("project-templates");
|
||||||
|
|
||||||
let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?;
|
let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?;
|
||||||
let plugin_version =
|
let plugin_version =
|
||||||
@@ -57,7 +58,9 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
"plugin version does not match Cargo version"
|
"plugin version does not match Cargo version"
|
||||||
);
|
);
|
||||||
|
|
||||||
let snapshot = VfsSnapshot::dir(hashmap! {
|
let template_snapshot = snapshot_from_fs_path(&templates_dir)?;
|
||||||
|
|
||||||
|
let plugin_snapshot = VfsSnapshot::dir(hashmap! {
|
||||||
"default.project.json" => snapshot_from_fs_path(&root_dir.join("plugin.project.json"))?,
|
"default.project.json" => snapshot_from_fs_path(&root_dir.join("plugin.project.json"))?,
|
||||||
"plugin" => VfsSnapshot::dir(hashmap! {
|
"plugin" => VfsSnapshot::dir(hashmap! {
|
||||||
"fmt" => snapshot_from_fs_path(&plugin_dir.join("fmt"))?,
|
"fmt" => snapshot_from_fs_path(&plugin_dir.join("fmt"))?,
|
||||||
@@ -70,10 +73,11 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
let out_path = Path::new(&out_dir).join("plugin.bincode");
|
let template_file = File::create(Path::new(&out_dir).join("templates.bincode"))?;
|
||||||
let out_file = File::create(out_path)?;
|
let plugin_file = File::create(Path::new(&out_dir).join("plugin.bincode"))?;
|
||||||
|
|
||||||
bincode::serialize_into(out_file, &snapshot)?;
|
bincode::serialize_into(plugin_file, &plugin_snapshot)?;
|
||||||
|
bincode::serialize_into(template_file, &template_snapshot)?;
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build/windows/rojo-manifest.rc");
|
println!("cargo:rerun-if-changed=build/windows/rojo-manifest.rc");
|
||||||
println!("cargo:rerun-if-changed=build/windows/rojo.manifest");
|
println!("cargo:rerun-if-changed=build/windows/rojo.manifest");
|
||||||
|
|||||||
233
src/cli/init.rs
233
src/cli/init.rs
@@ -1,30 +1,25 @@
|
|||||||
use std::io::{self, Write};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
io::{self, Write},
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{bail, format_err};
|
use anyhow::{bail, format_err};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use fs_err::OpenOptions;
|
use fs_err::OpenOptions;
|
||||||
|
use memofs::{InMemoryFs, Vfs, VfsSnapshot};
|
||||||
|
|
||||||
use super::resolve_path;
|
use super::resolve_path;
|
||||||
|
|
||||||
static MODEL_PROJECT: &str =
|
const GIT_IGNORE_PLACEHOLDER: &str = "gitignore.txt";
|
||||||
include_str!("../../assets/default-model-project/default.project.json");
|
|
||||||
static MODEL_README: &str = include_str!("../../assets/default-model-project/README.md");
|
|
||||||
static MODEL_INIT: &str = include_str!("../../assets/default-model-project/src-init.luau");
|
|
||||||
static MODEL_GIT_IGNORE: &str = include_str!("../../assets/default-model-project/gitignore.txt");
|
|
||||||
|
|
||||||
static PLACE_PROJECT: &str =
|
static TEMPLATE_BINCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/templates.bincode"));
|
||||||
include_str!("../../assets/default-place-project/default.project.json");
|
|
||||||
static PLACE_README: &str = include_str!("../../assets/default-place-project/README.md");
|
|
||||||
static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt");
|
|
||||||
|
|
||||||
static PLUGIN_PROJECT: &str =
|
|
||||||
include_str!("../../assets/default-plugin-project/default.project.json");
|
|
||||||
static PLUGIN_README: &str = include_str!("../../assets/default-plugin-project/README.md");
|
|
||||||
static PLUGIN_GIT_IGNORE: &str = include_str!("../../assets/default-plugin-project/gitignore.txt");
|
|
||||||
|
|
||||||
/// Initializes a new Rojo project.
|
/// Initializes a new Rojo project.
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
@@ -40,6 +35,8 @@ pub struct InitCommand {
|
|||||||
|
|
||||||
impl InitCommand {
|
impl InitCommand {
|
||||||
pub fn run(self) -> anyhow::Result<()> {
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
|
let template = self.kind.template();
|
||||||
|
|
||||||
let base_path = resolve_path(&self.path);
|
let base_path = resolve_path(&self.path);
|
||||||
fs::create_dir_all(&base_path)?;
|
fs::create_dir_all(&base_path)?;
|
||||||
|
|
||||||
@@ -53,10 +50,51 @@ impl InitCommand {
|
|||||||
name: project_name.to_owned(),
|
name: project_name.to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.kind {
|
println!(
|
||||||
InitKind::Place => init_place(&base_path, project_params)?,
|
"Creating new {:?} project '{}'",
|
||||||
InitKind::Model => init_model(&base_path, project_params)?,
|
self.kind, project_params.name
|
||||||
InitKind::Plugin => init_plugin(&base_path, project_params)?,
|
);
|
||||||
|
|
||||||
|
let vfs = Vfs::new(template);
|
||||||
|
vfs.set_watch_enabled(false);
|
||||||
|
|
||||||
|
let mut queue = VecDeque::with_capacity(8);
|
||||||
|
for entry in vfs.read_dir("")? {
|
||||||
|
queue.push_back(entry?.path().to_path_buf())
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(mut path) = queue.pop_front() {
|
||||||
|
let metadata = vfs.metadata(&path)?;
|
||||||
|
if metadata.is_dir() {
|
||||||
|
fs_err::create_dir(base_path.join(&path))?;
|
||||||
|
for entry in vfs.read_dir(&path)? {
|
||||||
|
queue.push_back(entry?.path().to_path_buf());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let content = vfs.read_to_string_lf_normalized(&path)?;
|
||||||
|
if let Some(file_stem) = path.file_name().and_then(OsStr::to_str) {
|
||||||
|
if file_stem == GIT_IGNORE_PLACEHOLDER {
|
||||||
|
path.set_file_name(".gitignore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write_if_not_exists(
|
||||||
|
&base_path.join(&path),
|
||||||
|
&project_params.render_template(&content),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_git_init(&base_path) {
|
||||||
|
log::debug!("Initializing Git repository...");
|
||||||
|
|
||||||
|
let status = Command::new("git")
|
||||||
|
.arg("init")
|
||||||
|
.current_dir(&base_path)
|
||||||
|
.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
bail!("git init failed: status code {:?}", status.code());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Created project successfully.");
|
println!("Created project successfully.");
|
||||||
@@ -78,6 +116,32 @@ pub enum InitKind {
|
|||||||
Plugin,
|
Plugin,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InitKind {
|
||||||
|
fn template(&self) -> InMemoryFs {
|
||||||
|
let template_path = match self {
|
||||||
|
Self::Place => "place",
|
||||||
|
Self::Model => "model",
|
||||||
|
Self::Plugin => "plugin",
|
||||||
|
};
|
||||||
|
|
||||||
|
let snapshot: VfsSnapshot = bincode::deserialize(TEMPLATE_BINCODE)
|
||||||
|
.expect("Rojo's templates were not properly packed into Rojo's binary");
|
||||||
|
|
||||||
|
if let VfsSnapshot::Dir { mut children } = snapshot {
|
||||||
|
if let Some(template) = children.remove(template_path) {
|
||||||
|
let mut fs = InMemoryFs::new();
|
||||||
|
fs.load_snapshot("", template)
|
||||||
|
.expect("loading a template in memory should never fail");
|
||||||
|
fs
|
||||||
|
} else {
|
||||||
|
panic!("template for project type {:?} is missing", self)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("Rojo's templates were packed as a file instead of a directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for InitKind {
|
impl FromStr for InitKind {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
@@ -94,92 +158,6 @@ impl FromStr for InitKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_place(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
|
||||||
println!("Creating new place project '{}'", project_params.name);
|
|
||||||
|
|
||||||
let project_file = project_params.render_template(PLACE_PROJECT);
|
|
||||||
try_create_project(base_path, &project_file)?;
|
|
||||||
|
|
||||||
let readme = project_params.render_template(PLACE_README);
|
|
||||||
write_if_not_exists(&base_path.join("README.md"), &readme)?;
|
|
||||||
|
|
||||||
let src = base_path.join("src");
|
|
||||||
fs::create_dir_all(&src)?;
|
|
||||||
|
|
||||||
let src_shared = src.join("shared");
|
|
||||||
fs::create_dir_all(src.join(&src_shared))?;
|
|
||||||
|
|
||||||
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_shared.join("Hello.luau"),
|
|
||||||
"return function()\n\tprint(\"Hello, world!\")\nend",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
write_if_not_exists(
|
|
||||||
&src_server.join("init.server.luau"),
|
|
||||||
"print(\"Hello world, from server!\")",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
write_if_not_exists(
|
|
||||||
&src_client.join("init.client.luau"),
|
|
||||||
"print(\"Hello world, from client!\")",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let git_ignore = project_params.render_template(PLACE_GIT_IGNORE);
|
|
||||||
try_git_init(base_path, &git_ignore)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_model(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
|
||||||
println!("Creating new model project '{}'", project_params.name);
|
|
||||||
|
|
||||||
let project_file = project_params.render_template(MODEL_PROJECT);
|
|
||||||
try_create_project(base_path, &project_file)?;
|
|
||||||
|
|
||||||
let readme = project_params.render_template(MODEL_README);
|
|
||||||
write_if_not_exists(&base_path.join("README.md"), &readme)?;
|
|
||||||
|
|
||||||
let src = base_path.join("src");
|
|
||||||
fs::create_dir_all(&src)?;
|
|
||||||
|
|
||||||
let init = project_params.render_template(MODEL_INIT);
|
|
||||||
write_if_not_exists(&src.join("init.luau"), &init)?;
|
|
||||||
|
|
||||||
let git_ignore = project_params.render_template(MODEL_GIT_IGNORE);
|
|
||||||
try_git_init(base_path, &git_ignore)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_plugin(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
|
||||||
println!("Creating new plugin project '{}'", project_params.name);
|
|
||||||
|
|
||||||
let project_file = project_params.render_template(PLUGIN_PROJECT);
|
|
||||||
try_create_project(base_path, &project_file)?;
|
|
||||||
|
|
||||||
let readme = project_params.render_template(PLUGIN_README);
|
|
||||||
write_if_not_exists(&base_path.join("README.md"), &readme)?;
|
|
||||||
|
|
||||||
let src = base_path.join("src");
|
|
||||||
fs::create_dir_all(&src)?;
|
|
||||||
|
|
||||||
write_if_not_exists(
|
|
||||||
&src.join("init.server.luau"),
|
|
||||||
"print(\"Hello world, from plugin!\")\n",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let git_ignore = project_params.render_template(PLUGIN_GIT_IGNORE);
|
|
||||||
try_git_init(base_path, &git_ignore)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Contains parameters used in templates to create a project.
|
/// Contains parameters used in templates to create a project.
|
||||||
struct ProjectParams {
|
struct ProjectParams {
|
||||||
name: String,
|
name: String,
|
||||||
@@ -194,23 +172,6 @@ impl ProjectParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to initialize a Git repository if necessary, and create .gitignore.
|
|
||||||
fn try_git_init(path: &Path, git_ignore: &str) -> Result<(), anyhow::Error> {
|
|
||||||
if should_git_init(path) {
|
|
||||||
log::debug!("Initializing Git repository...");
|
|
||||||
|
|
||||||
let status = Command::new("git").arg("init").current_dir(path).status()?;
|
|
||||||
|
|
||||||
if !status.success() {
|
|
||||||
bail!("git init failed: status code {:?}", status.code());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write_if_not_exists(&path.join(".gitignore"), git_ignore)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tells whether we should initialize a Git repository inside the given path.
|
/// Tells whether we should initialize a Git repository inside the given path.
|
||||||
///
|
///
|
||||||
/// Will return false if the user doesn't have Git installed or if the path is
|
/// Will return false if the user doesn't have Git installed or if the path is
|
||||||
@@ -251,29 +212,3 @@ fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), anyhow::Error>
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to create a project file and fail if it already exists.
|
|
||||||
fn try_create_project(base_path: &Path, contents: &str) -> Result<(), anyhow::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 => {
|
|
||||||
bail!("Project file already exists: {}", project_path.display())
|
|
||||||
}
|
|
||||||
_ => Err(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
file.write_all(contents.as_bytes())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user