diff --git a/README.md b/README.md index 7ea9292..0bfedca 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,9 @@ tarmac help [] * When a `tarmac.toml` file is found, Tarmac will include it and its includes and stop traversing that directory. ### InputConfig +* `name`, string, **optional** + * The name of the input + * Used for the TypeScript interface name (if `codegen-typescript-declaration` is specified - otherwise generated TS code will use **TarmacAssets**) * `glob`, string * A path glob that should include any files for this input group. * Tarmac uses the [globset library](https://docs.rs/globset/0.4.5/globset/) and supports any syntax it supports. @@ -202,6 +205,8 @@ tarmac help [] * If defined and `codegen` is true, Tarmac will merge all generated Lua code for this input group into a single file. * `codegen-base-path`, path, **optional** * Defines the base path for generating Lua code when `codegen-path` is also defined. Defaults to **the directory containing `tarmac.toml`**. +* `codegen-typescript-declaration`, bool, **optional** + * Will generate a corresponding `.d.ts` file alongside the generated Lua file if true. Defaults to **false**. ## License Tarmac is available under the MIT license. See [LICENSE.txt](LICENSE.txt) for details. diff --git a/examples/02-spritesheets/pack-these/index.d.ts b/examples/02-spritesheets/pack-these/index.d.ts index f2323c2..58b3e98 100644 --- a/examples/02-spritesheets/pack-these/index.d.ts +++ b/examples/02-spritesheets/pack-these/index.d.ts @@ -1,4 +1,4 @@ -/** This file was @generated by Tarmac. It is not intended for manual editing. */ +// This file was generated by Tarmac. It is not intended for manual editing. interface ImageSlice { readonly Image: string; readonly ImageRectOffset: Vector2; @@ -22,6 +22,8 @@ interface PackedSprites { f: ImageSlice; g: ImageSlice; } -/** Tarmac Generated Asset Types */ +/** + * Generated Tarmac Asset Interface + **/ declare const PackedSprites: PackedSprites; export = PackedSprites; diff --git a/src/codegen.rs b/src/codegen.rs index f069735..7bb2f2a 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,6 +1,10 @@ -use std::{path::{Path, self}, io, collections::BTreeMap}; +use std::{ + collections::BTreeMap, + io, + path::{self, Path}, +}; -use crate::{data::SyncInput, typescript, lua}; +use crate::{data::SyncInput, lua, typescript}; pub fn perform_codegen(output_path: Option<&Path>, inputs: &[&SyncInput]) -> io::Result<()> { lua::codegen::perform_codegen(output_path, inputs)?; @@ -22,7 +26,10 @@ pub enum GroupedItem<'a> { } impl GroupedItem<'_> { - pub fn parse_root_folder<'a>(output_path: &Path, inputs: &'a [&SyncInput]) -> BTreeMap> { + pub fn parse_root_folder<'a>( + output_path: &Path, + inputs: &'a [&SyncInput], + ) -> BTreeMap> { let mut root_folder: BTreeMap> = BTreeMap::new(); for &input in inputs { @@ -31,18 +38,18 @@ impl GroupedItem<'_> { if !input.config.codegen { continue; } - + // The extension portion of the path is not useful for code generation. // By stripping it off, we generate the names that users expect. let mut path_without_extension = input.path_without_dpi_scale.clone(); path_without_extension.set_extension(""); - + // If we can't construct a relative path, there isn't a sensible name // that we can use to refer to this input. let relative_path = path_without_extension .strip_prefix(&input.config.codegen_base_path) .expect("Input base path was not a base path for input"); - + // Collapse `..` path segments so that we can map this path onto our // tree of inputs. let mut segments = Vec::new(); @@ -57,14 +64,14 @@ impl GroupedItem<'_> { path::Component::ParentDir => assert!(segments.pop().is_some()), } } - + // Navigate down the tree, creating any folder entries that don't exist // yet. let mut current_dir = &mut root_folder; for (i, &segment) in segments.iter().enumerate() { if i == segments.len() - 1 { // We assume that the last segment of a path must be a file. - + let input_group = match current_dir.get_mut(segment) { Some(existing) => existing, None => { @@ -75,7 +82,7 @@ impl GroupedItem<'_> { current_dir.get_mut(segment).unwrap() } }; - + if let GroupedItem::InputGroup { inputs_by_dpi_scale, } = input_group @@ -85,13 +92,12 @@ impl GroupedItem<'_> { unreachable!(); } } else { - let next_entry = - current_dir - .entry(segment.to_owned()) - .or_insert_with(|| GroupedItem::Folder { - children_by_name: BTreeMap::new(), - }); - + let next_entry = current_dir.entry(segment.to_owned()).or_insert_with(|| { + GroupedItem::Folder { + children_by_name: BTreeMap::new(), + } + }); + if let GroupedItem::Folder { children_by_name } = next_entry { current_dir = children_by_name; } else { @@ -103,4 +109,4 @@ impl GroupedItem<'_> { root_folder } -} \ No newline at end of file +} diff --git a/src/lua/codegen.rs b/src/lua/codegen.rs index b07c920..3b09963 100644 --- a/src/lua/codegen.rs +++ b/src/lua/codegen.rs @@ -11,9 +11,10 @@ use std::{ use fs_err::File; use crate::{ + codegen::GroupedItem, data::ImageSlice, data::{AssetId, SyncInput}, - lua::lua_ast::{Block, Expression, Function, IfBlock, Statement, Table}, codegen::GroupedItem, + lua::lua_ast::{Block, Expression, Function, IfBlock, Statement, Table}, }; const CODEGEN_HEADER: &str = diff --git a/src/lua/mod.rs b/src/lua/mod.rs index 3a00196..c0b8a00 100644 --- a/src/lua/mod.rs +++ b/src/lua/mod.rs @@ -1,2 +1,2 @@ pub mod codegen; -pub mod lua_ast; \ No newline at end of file +pub mod lua_ast; diff --git a/src/main.rs b/src/main.rs index a681a20..7bf34cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,16 @@ mod alpha_bleed; mod asset_name; mod auth_cookie; +mod codegen; mod commands; mod data; mod dpi_scale; mod glob; +mod lua; mod options; mod roblox_web_api; mod sync_backend; -mod lua; mod typescript; -mod codegen; use std::{env, panic, process}; diff --git a/src/typescript/codegen.rs b/src/typescript/codegen.rs index 2d0d929..bca93ca 100644 --- a/src/typescript/codegen.rs +++ b/src/typescript/codegen.rs @@ -10,7 +10,7 @@ use fs_err::File; use crate::{ codegen::GroupedItem, data::SyncInput, - typescript::ts_ast::{Expression, VariableDeclaration, VariableKind}, + typescript::ts_ast::{Comment, Expression, VariableDeclaration, VariableKind}, }; use super::ts_ast::{ @@ -19,7 +19,7 @@ use super::ts_ast::{ }; const CODEGEN_HEADER: &str = - "/** This file was @generated by Tarmac. It is not intended for manual editing. */"; + "This file was generated by Tarmac. It is not intended for manual editing."; const CODEGEN_IMAGE_SLICE_INTERFACE: &str = "ImageSlice"; pub fn perform_codegen(output_path: Option<&Path>, inputs: &[&SyncInput]) -> io::Result<()> { @@ -181,7 +181,6 @@ fn codegen_grouped(output_path: &Path, inputs: &[&SyncInput]) -> io::Result<()> } let first = should_generate_d_ts.unwrap(); - let name = &first.config.name; let root_folder = GroupedItem::parse_root_folder(output_path, inputs); @@ -189,8 +188,8 @@ fn codegen_grouped(output_path: &Path, inputs: &[&SyncInput]) -> io::Result<()> children_by_name: root_folder, }; - let mut file = File::create(declaration_path)?; - writeln!(file, "{}", CODEGEN_HEADER)?; + let mut file = File::create(&declaration_path)?; + write!(file, "{}", Comment::singleline(CODEGEN_HEADER.into()))?; let (properties, prereqs) = get_properties(&root); @@ -219,23 +218,23 @@ fn codegen_grouped(output_path: &Path, inputs: &[&SyncInput]) -> io::Result<()> write!( file, "{}", - Statement::InterfaceDeclaration(assets_interface) - )?; - - write!( - file, - "/** Tarmac Generated Asset Types */\n{}", - VariableDeclaration::new( - sanitized_name.to_string(), - VariableKind::Const, - Some(Expression::Identifier(sanitized_name.to_string())), - Some(vec![ModifierToken::Declare]), - None, - ) + Statement::list(vec![ + // interface TarmacAssets { ... } + Statement::InterfaceDeclaration(assets_interface), + Comment::multiline("*\n * Generated Tarmac Asset Interface\n *".into()), + // declare const TarmacAssets: TarmacAssets + VariableDeclaration::new( + sanitized_name.to_string(), + VariableKind::Const, + Some(Expression::Identifier(sanitized_name.to_string())), + Some(vec![ModifierToken::Declare]), + None, + ), + // export = TarmacAssets; + export_assignment, + ]) )?; - write!(file, "{}", export_assignment)?; - Ok(()) } diff --git a/src/typescript/mod.rs b/src/typescript/mod.rs index 3920368..0630f5d 100644 --- a/src/typescript/mod.rs +++ b/src/typescript/mod.rs @@ -1,2 +1,2 @@ +pub mod codegen; pub mod ts_ast; -pub mod codegen; \ No newline at end of file diff --git a/src/typescript/ts_ast.rs b/src/typescript/ts_ast.rs index 8adad4e..bd76b72 100644 --- a/src/typescript/ts_ast.rs +++ b/src/typescript/ts_ast.rs @@ -1,4 +1,4 @@ -use std::fmt::{self, Write}; +use std::fmt::{self, Display, Write}; use fs_err::write; @@ -384,17 +384,38 @@ pub struct TypeAliasDeclaration { type_expression: Expression, } +pub enum Comment { + Single(String), + Multiline(String), +} + +impl Comment { + pub fn multiline(text: String) -> Statement { + Statement::Comment(Self::Multiline(text)) + } + + pub fn singleline(text: String) -> Statement { + Statement::Comment(Self::Single(text)) + } +} + pub(crate) enum Statement { InterfaceDeclaration(InterfaceDeclaration), TypeAliasDeclaration(TypeAliasDeclaration), VariableDeclaration(VariableDeclaration), ExportAssignment(ExportAssignment), + Comment(Comment), + List(Vec), } impl Statement { pub fn export_assignment(expression: Expression) -> Statement { Statement::ExportAssignment(ExportAssignment { expression }) } + + pub fn list(statements: Vec) -> Self { + Self::List(statements) + } } impl FmtTS for Statement { @@ -412,6 +433,21 @@ impl FmtTS for Statement { type_alias.name, type_alias.type_expression ) } + Self::Comment(comment) => match comment { + Comment::Single(text) => { + writeln!(output, "// {}", text) + } + Comment::Multiline(text) => { + writeln!(output, "/*{}*/", text) + } + }, + Self::List(statements) => { + for statement in statements { + statement.fmt_ts(output)?; + } + + Ok(()) + } } } }