diff --git a/Cargo.lock b/Cargo.lock index 9f2c1f6a..3d072dec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -920,6 +920,14 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-traits" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num_cpus" version = "1.10.1" @@ -1450,6 +1458,17 @@ dependencies = [ "snax 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rlua" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rojo" version = "0.6.0-dev" @@ -1477,6 +1496,7 @@ dependencies = [ "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.21 (registry+https://github.com/rust-lang/crates.io-index)", "ritz 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlua 0.16.3 (registry+https://github.com/rust-lang/crates.io-index)", "rojo-insta-ext 0.1.0", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2294,6 +2314,7 @@ dependencies = [ "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum notify 4.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1191efa2b8fe041decb55c238a125b7a1aeb6fad7a525133a02be5ec949ff3cb" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" @@ -2350,6 +2371,7 @@ dependencies = [ "checksum reqwest 0.9.21 (registry+https://github.com/rust-lang/crates.io-index)" = "02b7e953e14c6f3102b7e8d1f1ee3abf5ecee80b427f5565c9389835cecae95c" "checksum ritz 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b52479a28408dacd24819d32f3562146b5f03eb0a06a8b2d7b11e34fbfe52d" "checksum ritz_impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7bba143ce94ca7e580094b8c4f6338b960b3bfa5ad7f479700e414b24d5d7b" +"checksum rlua 0.16.3 (registry+https://github.com/rust-lang/crates.io-index)" = "62fc0e980c94fe9ef795b1bb3874649c8c6e9bb67d3b90d48380ba24c69c23ea" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" diff --git a/Cargo.toml b/Cargo.toml index e52c5fae..3408bb39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,10 @@ default = [] # Enable this feature to live-reload assets from the web UI. dev-live-assets = [] +# Enables specifying user plugins in the project file. User plugins are Lua +# files and currently not fully implemented. See issue #55. +user-plugins = [] + [workspace] members = [ "rojo-test", @@ -58,6 +62,7 @@ rbx_xml = "0.11.0" regex = "1.0" reqwest = "0.9.20" ritz = "0.1.0" +rlua = "0.16.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" termcolor = "1.0.5" diff --git a/src/change_processor.rs b/src/change_processor.rs index 533a982c..e0ad8496 100644 --- a/src/change_processor.rs +++ b/src/change_processor.rs @@ -12,7 +12,7 @@ use crate::{ imfs::{Imfs, ImfsEvent, ImfsFetcher}, message_queue::MessageQueue, snapshot::{apply_patch_set, compute_patch_set, AppliedPatchSet, InstigatingSource, RojoTree}, - snapshot_middleware::snapshot_from_imfs, + snapshot_middleware::{snapshot_from_imfs, InstanceSnapshotContext}, }; pub struct ChangeProcessor { @@ -99,7 +99,10 @@ impl ChangeProcessor { .get(path) .expect("could not get instigating path from filesystem"); - let snapshot = snapshot_from_imfs(&mut imfs, &entry) + // TODO: Use persisted snapshot + // context struct instead of + // recreating it every time. + let snapshot = snapshot_from_imfs(&mut InstanceSnapshotContext::default(), &mut imfs, &entry) .expect("snapshot failed") .expect("snapshot did not return an instance"); diff --git a/src/commands/build.rs b/src/commands/build.rs index f3dad7bd..de10ef34 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -11,7 +11,7 @@ use rbx_dom_weak::RbxInstanceProperties; use crate::{ imfs::{FsError, Imfs, RealFetcher, WatchMode}, snapshot::{apply_patch_set, compute_patch_set, InstancePropertiesWithMeta, RojoTree}, - snapshot_middleware::snapshot_from_imfs, + snapshot_middleware::{snapshot_from_imfs, InstanceSnapshotContext}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -96,8 +96,9 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> { .get(&options.fuzzy_project_path) .expect("could not get project path"); + // TODO: Compute snapshot context from project. log::trace!("Generating snapshot of instances from IMFS"); - let snapshot = snapshot_from_imfs(&mut imfs, &entry) + let snapshot = snapshot_from_imfs(&mut InstanceSnapshotContext::default(), &mut imfs, &entry) .expect("snapshot failed") .expect("snapshot did not return an instance"); diff --git a/src/commands/upload.rs b/src/commands/upload.rs index 0aaee43e..ded0446a 100644 --- a/src/commands/upload.rs +++ b/src/commands/upload.rs @@ -8,7 +8,7 @@ use crate::{ auth_cookie::get_auth_cookie, imfs::{Imfs, RealFetcher, WatchMode}, snapshot::{apply_patch_set, compute_patch_set, InstancePropertiesWithMeta, RojoTree}, - snapshot_middleware::snapshot_from_imfs, + snapshot_middleware::{snapshot_from_imfs, InstanceSnapshotContext}, }; #[derive(Debug, Fail)] @@ -63,8 +63,9 @@ pub fn upload(options: UploadOptions) -> Result<(), UploadError> { .get(&options.fuzzy_project_path) .expect("could not get project path"); + // TODO: Compute snapshot context from project. log::trace!("Generating snapshot of instances from IMFS"); - let snapshot = snapshot_from_imfs(&mut imfs, &entry) + let snapshot = snapshot_from_imfs(&mut InstanceSnapshotContext::default(), &mut imfs, &entry) .expect("snapshot failed") .expect("snapshot did not return an instance"); diff --git a/src/project.rs b/src/project.rs index ba0d6616..fa2e7cc8 100644 --- a/src/project.rs +++ b/src/project.rs @@ -30,6 +30,10 @@ struct SourceProject { #[serde(skip_serializing_if = "Option::is_none")] serve_place_ids: Option>, + + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[cfg_attr(not(feature = "user-plugins"), serde(skip_deserializing))] + plugins: Vec, } impl SourceProject { @@ -37,11 +41,19 @@ impl SourceProject { pub fn into_project(self, project_file_location: &Path) -> Project { let tree = self.tree.into_project_node(project_file_location); + let project_folder = project_file_location.parent().unwrap(); + let plugins = self + .plugins + .into_iter() + .map(|path| project_folder.join(path)) + .collect(); + Project { name: self.name, tree, serve_port: self.serve_port, serve_place_ids: self.serve_place_ids, + plugins, file_location: PathBuf::from(project_file_location), } } @@ -318,6 +330,7 @@ pub struct Project { pub tree: ProjectNode, pub serve_port: Option, pub serve_place_ids: Option>, + pub plugins: Vec, pub file_location: PathBuf, } @@ -391,6 +404,7 @@ impl Project { tree, serve_port: None, serve_place_ids: None, + plugins: Vec::new(), file_location: project_path.clone(), }; @@ -557,10 +571,24 @@ impl Project { } fn to_source_project(&self) -> SourceProject { + // TODO: Use path_serializer instead of transforming paths between + // String and PathBuf? + let plugins = self + .plugins + .iter() + .map(|path| { + path.strip_prefix(self.folder_location()) + .unwrap() + .display() + .to_string() + }) + .collect(); + SourceProject { name: self.name.clone(), tree: self.tree.to_source_node(&self.file_location), serve_port: self.serve_port, + plugins, serve_place_ids: self.serve_place_ids.clone(), } } diff --git a/src/serve_session.rs b/src/serve_session.rs index 30438c26..ebac8567 100644 --- a/src/serve_session.rs +++ b/src/serve_session.rs @@ -16,7 +16,7 @@ use crate::{ snapshot::{ apply_patch_set, compute_patch_set, AppliedPatchSet, InstancePropertiesWithMeta, RojoTree, }, - snapshot_middleware::snapshot_from_imfs, + snapshot_middleware::{snapshot_from_imfs, InstanceSnapshotContext}, }; /// Contains all of the state for a Rojo serve session. @@ -111,10 +111,12 @@ impl ServeSession { log::trace!("Loading start path: {}", start_path.display()); let entry = imfs.get(start_path).expect("could not get project path"); + // TODO: Compute snapshot context from project. log::trace!("Snapshotting start path"); - let snapshot = snapshot_from_imfs(&mut imfs, &entry) - .expect("snapshot failed") - .expect("snapshot did not return an instance"); + let snapshot = + snapshot_from_imfs(&mut InstanceSnapshotContext::default(), &mut imfs, &entry) + .expect("snapshot failed") + .expect("snapshot did not return an instance"); log::trace!("Computing initial patch set"); let patch_set = compute_patch_set(&snapshot, &tree, root_id); diff --git a/src/snapshot_middleware/context.rs b/src/snapshot_middleware/context.rs index b10e7192..0a7179a8 100644 --- a/src/snapshot_middleware/context.rs +++ b/src/snapshot_middleware/context.rs @@ -1,8 +1,12 @@ +use std::{fmt, ops::Deref, path::PathBuf}; + +use rlua::{Lua, RegistryKey}; + #[derive(Debug)] pub struct InstanceSnapshotContext { /// Empty struct that will be used later to fill out required Lua state for /// user plugins. - pub plugin_context: Option<()>, + pub plugin_context: Option, } impl Default for InstanceSnapshotContext { @@ -13,4 +17,47 @@ impl Default for InstanceSnapshotContext { } } +#[derive(Debug)] +pub struct SnapshotPluginContext { + pub state: IgnoreDebug, + + /// Paths to the user plugins files. These paths are generated by the root + /// project file, if there is one. + pub plugin_paths: Vec, + + /// Lazy-initialized registry keys pointing to the values returned by each + /// user plugin. When processing user plugins, these should be applied in + /// order. + pub plugin_functions: Option>, +} + +impl SnapshotPluginContext { + pub fn new(plugin_paths: Vec) -> Self { + Self { + state: IgnoreDebug(Lua::new()), + plugin_paths, + plugin_functions: None, + } + } +} + +/// Utility type to enable having a field of a struct not implement Debug and +/// instead show a placeholder. +#[derive(Clone)] +pub struct IgnoreDebug(pub T); + +impl fmt::Debug for IgnoreDebug { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "") + } +} + +impl Deref for IgnoreDebug { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + pub struct ImfsSnapshotContext; diff --git a/src/snapshot_middleware/dir.rs b/src/snapshot_middleware/dir.rs index 1c36dc6f..7f4cab39 100644 --- a/src/snapshot_middleware/dir.rs +++ b/src/snapshot_middleware/dir.rs @@ -18,7 +18,7 @@ pub struct SnapshotDir; impl SnapshotMiddleware for SnapshotDir { fn from_imfs( - _context: &mut InstanceSnapshotContext, + context: &mut InstanceSnapshotContext, imfs: &mut Imfs, entry: &ImfsEntry, ) -> SnapshotInstanceResult<'static> { @@ -31,7 +31,7 @@ impl SnapshotMiddleware for SnapshotDir { let mut snapshot_children = Vec::new(); for child in children.into_iter() { - if let Some(child_snapshot) = snapshot_from_imfs(imfs, &child)? { + if let Some(child_snapshot) = snapshot_from_imfs(context, imfs, &child)? { snapshot_children.push(child_snapshot); } } diff --git a/src/snapshot_middleware/error.rs b/src/snapshot_middleware/error.rs index 3f1805de..c2a72b59 100644 --- a/src/snapshot_middleware/error.rs +++ b/src/snapshot_middleware/error.rs @@ -16,6 +16,13 @@ impl SnapshotError { } } + pub(crate) fn wrap(inner: impl Into, path: impl Into) -> Self { + SnapshotError { + detail: inner.into(), + path: Some(path.into()), + } + } + pub(crate) fn file_did_not_exist(path: impl Into) -> SnapshotError { SnapshotError { detail: SnapshotErrorDetail::FileDidNotExist, @@ -70,27 +77,45 @@ impl From for SnapshotError { fn from(error: FsError) -> Self { let (inner, path) = error.into_raw(); - let detail = SnapshotErrorDetail::IoError { inner }; + Self::new(inner.into(), Some(path)) + } +} - Self::new(detail, Some(path)) +impl From for SnapshotError { + fn from(error: rlua::Error) -> Self { + Self::new(error.into(), Option::::None) } } #[derive(Debug)] pub enum SnapshotErrorDetail { IoError { inner: io::Error }, + Lua { inner: rlua::Error }, FileDidNotExist, FileNameBadUnicode, FileContentsBadUnicode { inner: std::str::Utf8Error }, MalformedProject { inner: serde_json::Error }, } +impl From for SnapshotErrorDetail { + fn from(inner: io::Error) -> Self { + SnapshotErrorDetail::IoError { inner } + } +} + +impl From for SnapshotErrorDetail { + fn from(inner: rlua::Error) -> Self { + SnapshotErrorDetail::Lua { inner } + } +} + impl SnapshotErrorDetail { fn source(&self) -> Option<&(dyn Error + 'static)> { use self::SnapshotErrorDetail::*; match self { IoError { inner } => Some(inner), + Lua { inner } => Some(inner), FileContentsBadUnicode { inner } => Some(inner), MalformedProject { inner } => Some(inner), _ => None, @@ -104,6 +129,7 @@ impl fmt::Display for SnapshotErrorDetail { match self { IoError { inner } => write!(formatter, "I/O error: {}", inner), + Lua { inner } => write!(formatter, "{}", inner), FileDidNotExist => write!(formatter, "file did not exist"), FileNameBadUnicode => write!(formatter, "file name had malformed Unicode"), FileContentsBadUnicode { inner } => { diff --git a/src/snapshot_middleware/mod.rs b/src/snapshot_middleware/mod.rs index 26968482..4809b87f 100644 --- a/src/snapshot_middleware/mod.rs +++ b/src/snapshot_middleware/mod.rs @@ -16,14 +16,15 @@ mod rbxlx; mod rbxm; mod rbxmx; mod txt; +mod user_plugins; mod util; +pub use self::context::*; pub use self::error::*; use rbx_dom_weak::{RbxId, RbxTree}; use self::{ - context::InstanceSnapshotContext, csv::SnapshotCsv, dir::SnapshotDir, json_model::SnapshotJsonModel, @@ -34,6 +35,7 @@ use self::{ rbxm::SnapshotRbxm, rbxmx::SnapshotRbxmx, txt::SnapshotTxt, + user_plugins::SnapshotUserPlugins, }; use crate::imfs::{Imfs, ImfsEntry, ImfsFetcher}; @@ -41,15 +43,14 @@ macro_rules! middlewares { ( $($middleware: ident,)* ) => { /// Generates a snapshot of instances from the given ImfsEntry. pub fn snapshot_from_imfs( + context: &mut InstanceSnapshotContext, imfs: &mut Imfs, entry: &ImfsEntry, ) -> SnapshotInstanceResult<'static> { - let mut context = InstanceSnapshotContext::default(); - $( log::trace!("trying middleware {} on {}", stringify!($middleware), entry.path().display()); - if let Some(snapshot) = $middleware::from_imfs(&mut context, imfs, entry)? { + if let Some(snapshot) = $middleware::from_imfs(context, imfs, entry)? { log::trace!("middleware {} success on {}", stringify!($middleware), entry.path().display()); return Ok(Some(snapshot)); } @@ -75,6 +76,7 @@ macro_rules! middlewares { middlewares! { SnapshotProject, + SnapshotUserPlugins, SnapshotJsonModel, SnapshotRbxlx, SnapshotRbxmx, diff --git a/src/snapshot_middleware/project.rs b/src/snapshot_middleware/project.rs index d1e0d9f4..c275d047 100644 --- a/src/snapshot_middleware/project.rs +++ b/src/snapshot_middleware/project.rs @@ -48,7 +48,8 @@ impl SnapshotMiddleware for SnapshotProject { // Snapshotting a project should always return an instance, so this // unwrap is safe. - let mut snapshot = snapshot_project_node(&project.name, &project.tree, imfs)?.unwrap(); + let mut snapshot = + snapshot_project_node(context, &project.name, &project.tree, imfs)?.unwrap(); // Setting the instigating source to the project file path is a little // coarse. @@ -76,6 +77,7 @@ impl SnapshotMiddleware for SnapshotProject { } fn snapshot_project_node( + context: &mut InstanceSnapshotContext, instance_name: &str, node: &ProjectNode, imfs: &mut Imfs, @@ -92,7 +94,7 @@ fn snapshot_project_node( if let Some(path) = &node.path { let entry = imfs.get(path)?; - if let Some(snapshot) = snapshot_from_imfs(imfs, &entry)? { + if let Some(snapshot) = snapshot_from_imfs(context, imfs, &entry)? { // If a class name was already specified, then it'll override the // class name of this snapshot ONLY if it's a Folder. // @@ -142,7 +144,7 @@ fn snapshot_project_node( .expect("$className or $path must be specified"); for (child_name, child_project_node) in &node.children { - if let Some(child) = snapshot_project_node(child_name, child_project_node, imfs)? { + if let Some(child) = snapshot_project_node(context, child_name, child_project_node, imfs)? { children.push(child); } } diff --git a/src/snapshot_middleware/user_plugins.rs b/src/snapshot_middleware/user_plugins.rs new file mode 100644 index 00000000..c98dda3b --- /dev/null +++ b/src/snapshot_middleware/user_plugins.rs @@ -0,0 +1,114 @@ +use std::{fs, path::Path}; + +use rlua::{Lua, RegistryKey}; + +use crate::imfs::{Imfs, ImfsEntry, ImfsFetcher}; + +use super::{ + context::InstanceSnapshotContext, + error::SnapshotError, + middleware::{SnapshotInstanceResult, SnapshotMiddleware}, +}; + +/// Handles snapshotting of any file that a user plugin wants to handle. +/// +/// User plugins are specified in the project file, but there are never user +/// plugins specified unless a Cargo feature is enabled, `user-plugins`. +/// Additionally, extra data needs to be set up inside the snapshot context +/// which is not currently wired up. +pub struct SnapshotUserPlugins; + +impl SnapshotMiddleware for SnapshotUserPlugins { + fn from_imfs( + context: &mut InstanceSnapshotContext, + _imfs: &mut Imfs, + _entry: &ImfsEntry, + ) -> SnapshotInstanceResult<'static> { + // User plugins are only enabled if present on the snapshot context. + let plugin_context = match &mut context.plugin_context { + Some(ctx) => ctx, + None => return Ok(None), + }; + + // If the plugins listed for use haven't been loaded yet, read them into + // memory, run them, and keep the result they return as a registry key + // into our Lua state. + let keys = match &plugin_context.plugin_functions { + Some(keys) => keys, + None => { + plugin_context.plugin_functions = Some(initialize_plugins( + &plugin_context.state, + &plugin_context.plugin_paths, + )?); + plugin_context.plugin_functions.as_ref().unwrap() + } + }; + + plugin_context.state.context(|lua_context| { + lua_context.scope(|_scope| { + for _key in keys { + // TODO: Invoke plugin here and get result out. + + // The current plan for plugins here is to make them work + // like Redux/Rodux middleware. A plugin will be a function + // that accepts the next middleware in the chain as a + // function and the snapshot subject (the IMFS entry). + // + // Plugins can (but don't have to) invoke the next snapshot + // function and may or may not mutate the result. The hope + // is that this model enables the most flexibility possible + // for plugins to modify existing Rojo output, as well as + // generate new outputs. + // + // Open questions: + // * How will middleware be ordered? Does putting user + // middleware always at the beginning or always at the end + // of the chain reduce the scope of what that middleware + // can do? + // + // * Will plugins hurt Rojo's ability to parallelize + // snapshotting in the future? + // + // * Do the mutable handles to the Imfs and the snapshot + // context prevent plugins from invoking other plugins + // indirectly? + // + // * Will there be problems using a single Lua state because + // of re-entrancy? + // + // * Can the Lua <-> Rojo bindings used for middleware be + // reused for or from another project like Remodel? + } + }) + }); + + Ok(None) + } +} + +fn initialize_plugins>( + lua_state: &Lua, + plugin_paths: &[P], +) -> Result, SnapshotError> { + plugin_paths + .iter() + .map(|path| { + let path = path.as_ref(); + + let content = fs::read_to_string(path).map_err(|err| SnapshotError::wrap(err, path))?; + + lua_state.context(|lua_context| { + // Plugins are currently expected to return a function that will + // be run when a snapshot needs to be generated. + let result = lua_context + .load(&content) + .set_name(&path.display().to_string())? + .call::<_, rlua::Function>(())?; + + let key = lua_context.create_registry_value(result)?; + + Ok(key) + }) + }) + .collect::, _>>() +} diff --git a/test-projects/plugins/default.project.json b/test-projects/plugins/default.project.json new file mode 100644 index 00000000..02387e7e --- /dev/null +++ b/test-projects/plugins/default.project.json @@ -0,0 +1,9 @@ +{ + "name": "plugins", + "tree": { + "$path": "src" + }, + "plugins": [ + "test-plugin.lua" + ] +} \ No newline at end of file diff --git a/test-projects/plugins/src/hello.moon b/test-projects/plugins/src/hello.moon new file mode 100644 index 00000000..ddb781cf --- /dev/null +++ b/test-projects/plugins/src/hello.moon @@ -0,0 +1 @@ +print 'Hello, world!' \ No newline at end of file diff --git a/test-projects/plugins/test-plugin.lua b/test-projects/plugins/test-plugin.lua new file mode 100644 index 00000000..147463cb --- /dev/null +++ b/test-projects/plugins/test-plugin.lua @@ -0,0 +1,20 @@ +print("test-plugin initializing...") + +return function(nextDispatch, entry) + if entry:isDirectory() then + return nextDispatch(entry) + end + + local name = entry:fileName() + local instanceName = name:match("(.-)%.moon$") + + if instanceName == nil then + return nextDispatch(entry) + end + + return rojo.instance({ + Name = instanceName, + ClassName = "ModuleScript", + Source = compileMoonScript(entry:contents()), + }) +end \ No newline at end of file diff --git a/tests/read_projects.rs b/tests/read_projects.rs index 55f0e1da..25844b9b 100644 --- a/tests/read_projects.rs +++ b/tests/read_projects.rs @@ -87,6 +87,7 @@ fn single_partition_game() { tree: root_node, serve_port: None, serve_place_ids: None, + plugins: Vec::new(), file_location: project_location.join("default.project.json"), } };