diff --git a/crates/cli/src/commands/generate.rs b/crates/cli/src/commands/generate.rs index e2150295f23..193bdef912f 100644 --- a/crates/cli/src/commands/generate.rs +++ b/crates/cli/src/commands/generate.rs @@ -431,7 +431,13 @@ pub async fn generate(name: String, options: GenerateOptions) -> AppResult { FileState::Replace => color::muted("->"), _ => color::muted("-->"), }, - color::muted_light(relative_dest.join(file.name).as_str()) + color::muted_light( + file.dest_path + .strip_prefix(&cwd) + .unwrap_or(&file.dest_path) + .to_str() + .unwrap() + ) ))?; } diff --git a/crates/cli/tests/generate_test.rs b/crates/cli/tests/generate_test.rs index 7106c1a0421..88cb4dd58f9 100644 --- a/crates/cli/tests/generate_test.rs +++ b/crates/cli/tests/generate_test.rs @@ -155,6 +155,20 @@ fn renders_with_custom_vars_via_args() { assert_snapshot!(fs::read_to_string(sandbox.path().join("./test/control.txt")).unwrap()); } +#[test] +fn handles_raw_files() { + let sandbox = generate_sandbox(); + + let assert = sandbox.run_moon(|cmd| { + cmd.arg("generate").arg("standard").arg("./test"); + }); + + assert.success(); + + assert_snapshot!(fs::read_to_string(sandbox.path().join("./test/file.txt")).unwrap()); + assert_snapshot!(fs::read_to_string(sandbox.path().join("./test/other.txt")).unwrap()); +} + #[test] fn interpolates_destination_path() { let sandbox = generate_sandbox(); diff --git a/crates/cli/tests/snapshots/generate_test__doesnt_generate_files_when_dryrun.snap b/crates/cli/tests/snapshots/generate_test__doesnt_generate_files_when_dryrun.snap index f0c13a3042c..89e7db61c1f 100644 --- a/crates/cli/tests/snapshots/generate_test__doesnt_generate_files_when_dryrun.snap +++ b/crates/cli/tests/snapshots/generate_test__doesnt_generate_files_when_dryrun.snap @@ -1,16 +1,16 @@ --- source: crates/cli/tests/generate_test.rs -assertion_line: 56 -expression: get_path_safe_output(&assert) +expression: assert.output_standardized() --- Template title (dry run) Some description of the template and its files. -created --> ./test/file.ts -created --> ./test/file.txt -created --> ./test/folder/nested-file.ts +created --> test/file.ts +created --> test/file.txt +created --> test/folder/nested-file.ts +created --> test/other.txt diff --git a/crates/cli/tests/snapshots/generate_test__generates_files_from_template.snap b/crates/cli/tests/snapshots/generate_test__generates_files_from_template.snap index 0c3dbbd6623..7dcaa86116a 100644 --- a/crates/cli/tests/snapshots/generate_test__generates_files_from_template.snap +++ b/crates/cli/tests/snapshots/generate_test__generates_files_from_template.snap @@ -1,16 +1,16 @@ --- source: crates/cli/tests/generate_test.rs -assertion_line: 37 -expression: get_path_safe_output(&assert) +expression: assert.output_standardized() --- Template title Some description of the template and its files. -created --> ./test/file.ts -created --> ./test/file.txt -created --> ./test/folder/nested-file.ts +created --> test/file.ts +created --> test/file.txt +created --> test/folder/nested-file.ts +created --> test/other.txt diff --git a/crates/cli/tests/snapshots/generate_test__handles_raw_files-2.snap b/crates/cli/tests/snapshots/generate_test__handles_raw_files-2.snap new file mode 100644 index 00000000000..6e8f2773b7c --- /dev/null +++ b/crates/cli/tests/snapshots/generate_test__handles_raw_files-2.snap @@ -0,0 +1,7 @@ +--- +source: crates/cli/tests/generate_test.rs +expression: "fs::read_to_string(sandbox.path().join(\"./test/other.txt\")).unwrap()" +--- +{% set my_var = 2 %} +{{ my_var }} + diff --git a/crates/cli/tests/snapshots/generate_test__handles_raw_files.snap b/crates/cli/tests/snapshots/generate_test__handles_raw_files.snap new file mode 100644 index 00000000000..c02762da1f2 --- /dev/null +++ b/crates/cli/tests/snapshots/generate_test__handles_raw_files.snap @@ -0,0 +1,6 @@ +--- +source: crates/cli/tests/generate_test.rs +expression: "fs::read_to_string(sandbox.path().join(\"./test/file.txt\")).unwrap()" +--- +2 + diff --git a/crates/cli/tests/snapshots/generate_test__interpolates_destination_path.snap b/crates/cli/tests/snapshots/generate_test__interpolates_destination_path.snap index 07ca57cac1d..8f4507a0a60 100644 --- a/crates/cli/tests/snapshots/generate_test__interpolates_destination_path.snap +++ b/crates/cli/tests/snapshots/generate_test__interpolates_destination_path.snap @@ -1,17 +1,16 @@ --- source: crates/cli/tests/generate_test.rs -assertion_line: 174 -expression: get_path_safe_output(&assert) +expression: assert.output_standardized() --- Variable testing A template for testing all variable config combinations. -created --> ./test/control.txt -created --> ./test/expressions.txt -created --> ./test/file-default-0.txt -created --> ./test/filters.txt +created --> test/control.txt +created --> test/expressions.txt +created --> test/file-default-0.txt +created --> test/filters.txt diff --git a/crates/cli/tests/snapshots/generate_test__overwrites_existing_files_when_forced.snap b/crates/cli/tests/snapshots/generate_test__overwrites_existing_files_when_forced.snap index dafc3c858b4..0d00756989e 100644 --- a/crates/cli/tests/snapshots/generate_test__overwrites_existing_files_when_forced.snap +++ b/crates/cli/tests/snapshots/generate_test__overwrites_existing_files_when_forced.snap @@ -1,16 +1,16 @@ --- source: crates/cli/tests/generate_test.rs -assertion_line: 81 -expression: get_path_safe_output(&assert) +expression: assert.output_standardized() --- Template title Some description of the template and its files. -replaced -> ./test/file.ts -replaced -> ./test/file.txt -replaced -> ./test/folder/nested-file.ts +replaced -> test/file.ts +replaced -> test/file.txt +replaced -> test/folder/nested-file.ts +replaced -> test/other.txt diff --git a/crates/cli/tests/snapshots/generate_test__overwrites_existing_files_when_interpolated_path.snap b/crates/cli/tests/snapshots/generate_test__overwrites_existing_files_when_interpolated_path.snap index cc05bde3e41..f3bccadce3a 100644 --- a/crates/cli/tests/snapshots/generate_test__overwrites_existing_files_when_interpolated_path.snap +++ b/crates/cli/tests/snapshots/generate_test__overwrites_existing_files_when_interpolated_path.snap @@ -1,17 +1,16 @@ --- source: crates/cli/tests/generate_test.rs -assertion_line: 108 -expression: get_path_safe_output(&assert) +expression: assert.output_standardized() --- Variable testing A template for testing all variable config combinations. -replaced -> ./test/control.txt -replaced -> ./test/expressions.txt -replaced -> ./test/file-default-0.txt -replaced -> ./test/filters.txt +replaced -> test/control.txt +replaced -> test/expressions.txt +replaced -> test/file-default-0.txt +replaced -> test/filters.txt diff --git a/crates/core/archive/tests/tree_differ_test.rs b/crates/core/archive/tests/tree_differ_test.rs index 53b078a62a0..c3920f67e09 100644 --- a/crates/core/archive/tests/tree_differ_test.rs +++ b/crates/core/archive/tests/tree_differ_test.rs @@ -8,7 +8,7 @@ fn loads_all_files() { let sandbox = create_sandbox("generator"); let differ = TreeDiffer::load(sandbox.path(), &string_vec!["templates"]).unwrap(); - assert_eq!(differ.files.len(), 21); + assert_eq!(differ.files.len(), 22); } #[test] @@ -16,7 +16,7 @@ fn loads_using_globs() { let sandbox = create_sandbox("generator"); let differ = TreeDiffer::load(sandbox.path(), &string_vec!["templates/**/*"]).unwrap(); - assert_eq!(differ.files.len(), 21); + assert_eq!(differ.files.len(), 22); } #[test] diff --git a/nextgen/codegen/src/template.rs b/nextgen/codegen/src/template.rs index b74690f1c1e..bb30d9cf5f4 100644 --- a/nextgen/codegen/src/template.rs +++ b/nextgen/codegen/src/template.rs @@ -100,12 +100,16 @@ impl Template { // Do a second pass and render the content for file in &mut files { file.set_content( - self.engine - .render(file.name.as_str(), context) - .map_err(|error| CodegenError::RenderTemplateFileFailed { - path: file.source_path.clone(), - error, - })?, + if file.raw { + fs::read_file(&file.source_path)? + } else { + self.engine + .render(file.name.as_str(), context) + .map_err(|error| CodegenError::RenderTemplateFileFailed { + path: file.source_path.clone(), + error, + })? + }, dest, )?; } diff --git a/nextgen/codegen/src/template_file.rs b/nextgen/codegen/src/template_file.rs index ed7a8e42fe6..b934b880189 100644 --- a/nextgen/codegen/src/template_file.rs +++ b/nextgen/codegen/src/template_file.rs @@ -25,6 +25,9 @@ pub struct TemplateFile { /// Relative path from templates dir. Also acts as the Tera engine name. pub name: RelativePathBuf, + /// Whether the file should not be rendered. + pub raw: bool, + /// Absolute path to source (in templates dir). pub source_path: PathBuf, @@ -35,6 +38,7 @@ pub struct TemplateFile { impl TemplateFile { pub fn new(name: RelativePathBuf, source_path: PathBuf) -> Self { TemplateFile { + raw: name.as_str().contains(".raw"), config: None, content: String::new(), dest_path: PathBuf::new(), @@ -73,7 +77,11 @@ impl TemplateFile { pub fn set_content>(&mut self, content: T, dest: &Path) -> miette::Result<()> { let content = content.as_ref().trim_start(); - self.dest_path = self.name.to_path(dest); + self.dest_path = if self.raw { + dest.join(self.name.as_str().replace(".raw", "")) + } else { + self.name.to_path(dest) + }; if content.starts_with("---") { debug!( diff --git a/nextgen/codegen/tests/__fixtures__/template/file.raw.txt b/nextgen/codegen/tests/__fixtures__/template/file.raw.txt new file mode 100644 index 00000000000..ed3946682e9 --- /dev/null +++ b/nextgen/codegen/tests/__fixtures__/template/file.raw.txt @@ -0,0 +1,2 @@ +{% set my_var = 2 %} +{{ my_var }} diff --git a/nextgen/codegen/tests/__fixtures__/template/file.txt b/nextgen/codegen/tests/__fixtures__/template/file.txt index d95f3ad14de..ed3946682e9 100644 --- a/nextgen/codegen/tests/__fixtures__/template/file.txt +++ b/nextgen/codegen/tests/__fixtures__/template/file.txt @@ -1 +1,2 @@ -content +{% set my_var = 2 %} +{{ my_var }} diff --git a/nextgen/codegen/tests/template_file_test.rs b/nextgen/codegen/tests/template_file_test.rs index f4d0061180c..17c59295b7e 100644 --- a/nextgen/codegen/tests/template_file_test.rs +++ b/nextgen/codegen/tests/template_file_test.rs @@ -6,6 +6,21 @@ use std::path::PathBuf; mod template_file { use super::*; + #[test] + fn marks_as_raw() { + let template = TemplateFile::new(RelativePathBuf::from("file.raw.txt"), PathBuf::new()); + + assert!(template.raw); + + let template = TemplateFile::new(RelativePathBuf::from("file.raw"), PathBuf::new()); + + assert!(template.raw); + + let template = TemplateFile::new(RelativePathBuf::from("file.txt.raw"), PathBuf::new()); + + assert!(template.raw); + } + mod mergeable { use super::*; @@ -56,6 +71,19 @@ mod template_file { template } + #[test] + fn removes_raw_from_path() { + let mut template = + TemplateFile::new(RelativePathBuf::from("file.raw.js"), PathBuf::new()); + template + .set_content("{{ foo }}", &PathBuf::from("root")) + .unwrap(); + + assert_eq!(template.content, "{{ foo }}"); + assert_eq!(template.config, None); + assert_eq!(template.dest_path, PathBuf::from("root/file.js")); + } + #[test] fn sets_no_frontmatter() { let template = create_file_with_content("export {};"); diff --git a/nextgen/codegen/tests/template_test.rs b/nextgen/codegen/tests/template_test.rs index e5b5051a310..001519834cc 100644 --- a/nextgen/codegen/tests/template_test.rs +++ b/nextgen/codegen/tests/template_test.rs @@ -41,6 +41,7 @@ mod template { .map(|f| f.source_path) .collect::>(), vec![ + fixture.join("file.raw.txt"), fixture.join("file.ts"), fixture.join("file.txt"), fixture.join("folder/nested-file.ts") @@ -61,6 +62,7 @@ mod template { assert_eq!( files, vec![ + "file.raw.txt", "file.ts", "file.txt", "folder/nested-file.ts", @@ -76,7 +78,7 @@ mod template { template.load_files(&fixture, &create_context()).unwrap(); - let file = &template.files[0]; + let file = template.files.iter().find(|f| f.name == "file.ts").unwrap(); assert_eq!(file.content, "export {};\n"); assert_eq!( @@ -86,6 +88,31 @@ mod template { ..TemplateFrontmatterConfig::default() }) ); + + let file = template + .files + .iter() + .find(|f| f.name == "file.txt") + .unwrap(); + + assert_eq!(file.content, "2\n"); + assert_eq!(file.config, None); + } + + #[test] + fn doesnt_render_raw_files() { + let mut template = create_template(); + let fixture = locate_fixture("template"); + + template.load_files(&fixture, &create_context()).unwrap(); + + let file = template + .files + .iter() + .find(|f| f.name == "file.raw.txt") + .unwrap(); + + assert_eq!(file.content, "{% set my_var = 2 %}\n{{ my_var }}\n"); } #[test] diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index c80d2fdedc4..3825d1ad2c9 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -5,6 +5,9 @@ #### 🚀 Updates - Identifiers (project names, file groups, etc) can now be prefixed with underscores (`_`). +- **Codegen** + - Templates can be used as-is without rendering with [Tera](https://tera.netlify.app) by appending + a `.raw` extension. #### 🐞 Fixes diff --git a/tests/fixtures/generator/templates/standard/file.txt b/tests/fixtures/generator/templates/standard/file.txt index d95f3ad14de..ed3946682e9 100644 --- a/tests/fixtures/generator/templates/standard/file.txt +++ b/tests/fixtures/generator/templates/standard/file.txt @@ -1 +1,2 @@ -content +{% set my_var = 2 %} +{{ my_var }} diff --git a/tests/fixtures/generator/templates/standard/other.raw.txt b/tests/fixtures/generator/templates/standard/other.raw.txt new file mode 100644 index 00000000000..ed3946682e9 --- /dev/null +++ b/tests/fixtures/generator/templates/standard/other.raw.txt @@ -0,0 +1,2 @@ +{% set my_var = 2 %} +{{ my_var }} diff --git a/website/docs/guides/codegen.mdx b/website/docs/guides/codegen.mdx index fb205fd9d8b..c4f863781e3 100644 --- a/website/docs/guides/codegen.mdx +++ b/website/docs/guides/codegen.mdx @@ -4,6 +4,8 @@ toc_max_heading_level: 6 tags: [codegen, generator, scaffold, template] --- +import VersionLabel from '@site/src/components/Docs/VersionLabel'; + Code generation provides an easy mechanism for automating common development workflows and file structures. Whether it's scaffolding a new library or application, updating configuration, or standardizing patterns. @@ -88,6 +90,14 @@ be generated into the target destination, and _do not_ support frontmatter. To ensure they are not generated, include the word "partial" anywhere in the file path. For example, `partials/header.tpl` or `header.partial.tpl`. +#### Raws + +Raw template files are another special type of file that bypass all Tera rendering, and are used +as-is instead. This is useful for files that contain syntax that conflicts with Tera. + +To mark a file as raw, add a `.raw` extension, for example: `file.raw.js` or `file.js.raw`. When the +file is generated, the `.raw` extension will be removed. + #### Frontmatter Frontmatter is a well-known concept for "per file configuration", and is achieved by inserting YAML