forked from rojo-rbx/rojo
Implement sourcemap CLI command (#530)
* Initial implementation of sourcemap CLI command * Update src/cli/sourcemap.rs Co-authored-by: JohnnyMorganz <johnnymorganz@outlook.com> * Update src/cli/sourcemap.rs Co-authored-by: JohnnyMorganz <johnnymorganz@outlook.com> * Tidy up sourcemap command * Update CHANGELOG Co-authored-by: JohnnyMorganz <johnnymorganz@outlook.com> Co-authored-by: Lucien Greathouse <me@lpghatguy.com>
This commit is contained in:
@@ -3,9 +3,11 @@
|
||||
## Unreleased Changes
|
||||
* Added support for specifying an address to be used by default in the .project.json file ([#447])
|
||||
* Added support for the new Open Cloud API when uploading. ([#486])
|
||||
* Added `sourcemap` command for generating sourcemaps to feed into other tools. ([#530])
|
||||
|
||||
[#447]: https://github.com/rojo-rbx/rojo/issues/447
|
||||
[#486]: https://github.com/rojo-rbx/rojo/issues/486
|
||||
[#530]: https://github.com/rojo-rbx/rojo/pull/530
|
||||
|
||||
## [7.0.0] - December 10, 2021
|
||||
* Fixed Rojo's interactions with properties enabled by FFlags that are not yet enabled. ([#493])
|
||||
@@ -480,4 +482,4 @@ This is a general maintenance release for the Rojo 0.5.x release series.
|
||||
* More robust syncing with a new reconciler
|
||||
|
||||
## [0.1.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.1.0) (November 29, 2017)
|
||||
* Initial release, functionally very similar to [rbxfs](https://github.com/LPGhatguy/rbxfs)
|
||||
* Initial release, functionally very similar to [rbxfs](https://github.com/LPGhatguy/rbxfs)
|
||||
@@ -6,6 +6,7 @@ mod fmt_project;
|
||||
mod init;
|
||||
mod plugin;
|
||||
mod serve;
|
||||
mod sourcemap;
|
||||
mod upload;
|
||||
|
||||
use std::{borrow::Cow, env, path::Path, str::FromStr};
|
||||
@@ -19,6 +20,7 @@ pub use self::fmt_project::FmtProjectCommand;
|
||||
pub use self::init::{InitCommand, InitKind};
|
||||
pub use self::plugin::{PluginCommand, PluginSubcommand};
|
||||
pub use self::serve::ServeCommand;
|
||||
pub use self::sourcemap::SourcemapCommand;
|
||||
pub use self::upload::UploadCommand;
|
||||
|
||||
/// Command line options that Rojo accepts, defined using the structopt crate.
|
||||
@@ -40,6 +42,7 @@ impl Options {
|
||||
Subcommand::Serve(subcommand) => subcommand.run(self.global),
|
||||
Subcommand::Build(subcommand) => subcommand.run(),
|
||||
Subcommand::Upload(subcommand) => subcommand.run(),
|
||||
Subcommand::Sourcemap(subcommand) => subcommand.run(),
|
||||
Subcommand::FmtProject(subcommand) => subcommand.run(),
|
||||
Subcommand::Doc(subcommand) => subcommand.run(),
|
||||
Subcommand::Plugin(subcommand) => subcommand.run(),
|
||||
@@ -112,6 +115,7 @@ pub enum Subcommand {
|
||||
Serve(ServeCommand),
|
||||
Build(BuildCommand),
|
||||
Upload(UploadCommand),
|
||||
Sourcemap(SourcemapCommand),
|
||||
FmtProject(FmtProjectCommand),
|
||||
Doc(DocCommand),
|
||||
Plugin(PluginCommand),
|
||||
|
||||
139
src/cli/sourcemap.rs
Normal file
139
src/cli/sourcemap.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
use std::{
|
||||
io::{BufWriter, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use fs_err::File;
|
||||
use memofs::Vfs;
|
||||
use rbx_dom_weak::types::Ref;
|
||||
use serde::Serialize;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use crate::{
|
||||
serve_session::ServeSession,
|
||||
snapshot::{InstanceWithMeta, RojoTree},
|
||||
};
|
||||
|
||||
use super::resolve_path;
|
||||
|
||||
const PATH_STRIP_FAILED_ERR: &str = "Failed to create relative paths for project file!";
|
||||
|
||||
/// Representation of a node in the generated sourcemap tree.
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SourcemapNode {
|
||||
name: String,
|
||||
class_name: String,
|
||||
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
file_paths: Vec<PathBuf>,
|
||||
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
children: Vec<SourcemapNode>,
|
||||
}
|
||||
|
||||
/// Generates a sourcemap file from the Rojo project.
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct SourcemapCommand {
|
||||
/// Path to the project to use for the sourcemap. Defaults to the current
|
||||
/// directory.
|
||||
#[structopt(default_value = "")]
|
||||
pub project: PathBuf,
|
||||
|
||||
/// Where to output the sourcemap. Omit this to use stdout instead of
|
||||
/// writing to a file.
|
||||
///
|
||||
/// Should end in .json.
|
||||
#[structopt(long, short)]
|
||||
pub output: Option<PathBuf>,
|
||||
|
||||
/// If non-script files should be included or not. Defaults to false.
|
||||
#[structopt(long)]
|
||||
pub include_non_scripts: bool,
|
||||
}
|
||||
|
||||
impl SourcemapCommand {
|
||||
pub fn run(self) -> anyhow::Result<()> {
|
||||
let project_path = resolve_path(&self.project);
|
||||
|
||||
let mut project_dir = project_path.to_path_buf();
|
||||
project_dir.pop();
|
||||
|
||||
log::trace!("Constructing in-memory filesystem");
|
||||
let vfs = Vfs::new_default();
|
||||
|
||||
let session = ServeSession::new(vfs, &project_path)?;
|
||||
let tree = session.tree();
|
||||
|
||||
let filter = if self.include_non_scripts {
|
||||
filter_nothing
|
||||
} else {
|
||||
filter_non_scripts
|
||||
};
|
||||
|
||||
let root_node = recurse_create_node(&tree, tree.get_root_id(), &project_dir, filter);
|
||||
|
||||
if let Some(output_path) = self.output {
|
||||
let mut file = BufWriter::new(File::create(&output_path)?);
|
||||
serde_json::to_writer(&mut file, &root_node)?;
|
||||
file.flush()?;
|
||||
|
||||
println!("Created sourcemap at {}", output_path.display());
|
||||
} else {
|
||||
let output = serde_json::to_string(&root_node)?;
|
||||
println!("{}", output);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_nothing(_instance: &InstanceWithMeta) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn filter_non_scripts(instance: &InstanceWithMeta) -> bool {
|
||||
match instance.class_name() {
|
||||
"Script" | "LocalScript" | "ModuleScript" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn recurse_create_node(
|
||||
tree: &RojoTree,
|
||||
referent: Ref,
|
||||
project_dir: &Path,
|
||||
filter: fn(&InstanceWithMeta) -> bool,
|
||||
) -> Option<SourcemapNode> {
|
||||
let instance = tree.get_instance(referent).expect("instance did not exist");
|
||||
|
||||
let mut children = Vec::new();
|
||||
for &child_id in instance.children() {
|
||||
if let Some(child_node) = recurse_create_node(tree, child_id, &project_dir, filter) {
|
||||
children.push(child_node);
|
||||
}
|
||||
}
|
||||
|
||||
// If this object has no children and doesn't pass the filter, it doesn't
|
||||
// contain any information we're looking for.
|
||||
if children.is_empty() && !filter(&instance) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let file_paths = instance
|
||||
.metadata()
|
||||
.relevant_paths
|
||||
.iter()
|
||||
// Not all paths listed as relevant are guaranteed to exist.
|
||||
.filter(|path| path.is_file())
|
||||
.map(|path| path.strip_prefix(project_dir).expect(PATH_STRIP_FAILED_ERR))
|
||||
.map(|path| path.to_path_buf())
|
||||
.collect();
|
||||
|
||||
Some(SourcemapNode {
|
||||
name: instance.name().to_string(),
|
||||
class_name: instance.class_name().to_string(),
|
||||
file_paths,
|
||||
children,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user