From 28ae870dab175264a87d1b1020bcbf23c85a60c1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 1 Dec 2017 13:44:04 -0700 Subject: [PATCH] feat: Migrate from _layouts to _includes This makes it so `cobalt migrate` will copy files from `_layouts` to `_includes` to suppress the deprecation warnings about not going `{% include "_layouts/file" %}`. I took a conservative approach here, so the snippets are still left in `_layouts` and layouts are copied to `_includes`. Manual intervention will be needed. --- src/bin/cobalt/main.rs | 1 + src/bin/cobalt/migrate.rs | 86 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/bin/cobalt/main.rs b/src/bin/cobalt/main.rs index 13e40d16..b1679d64 100644 --- a/src/bin/cobalt/main.rs +++ b/src/bin/cobalt/main.rs @@ -55,6 +55,7 @@ extern crate env_logger; extern crate ghp; extern crate hyper; extern crate notify; +extern crate regex; extern crate serde_yaml; #[macro_use] diff --git a/src/bin/cobalt/migrate.rs b/src/bin/cobalt/migrate.rs index 6ba33012..70c46c38 100644 --- a/src/bin/cobalt/migrate.rs +++ b/src/bin/cobalt/migrate.rs @@ -1,8 +1,10 @@ use std::env; +use std::ffi; use std::path; use clap; use cobalt; +use regex; use serde_yaml; use args; @@ -15,13 +17,13 @@ pub fn migrate_command_args() -> clap::App<'static, 'static> { } pub fn migrate_command(matches: &clap::ArgMatches) -> Result<()> { - info!("Migrating"); - migrate_config(matches.value_of("config")) .chain_err(|| "Failed to migrate config")?; let config = args::get_config(matches)?; - let _config = config.build()?; + let config = config.build()?; + + migrate_includes(config)?; Ok(()) } @@ -35,6 +37,7 @@ fn migrate_config(config_path: Option<&str>) -> Result<()> { .unwrap_or_else(|| cwd.join(".cobalt.yml")); config_path }; + info!("Migrating {:?}", config_path); let content = cobalt::cobalt_model::files::read_file(&config_path); let config = if let Ok(content) = content { @@ -49,3 +52,80 @@ fn migrate_config(config_path: Option<&str>) -> Result<()> { Ok(()) } + +fn migrate_includes_path(content: String) -> Result { + lazy_static!{ + static ref REPLACEMENTS_REF: Vec<(regex::Regex, &'static str)> = vec![ + (r#"\{%\s*include\s*['"]_layouts/(.*?)['"]\s*%}"#, r#"{% include "$1" %}"#), + (r#"\{\{\s*include\s*['"]_layouts/(.*?)['"]\s*}}"#, r#"{% include "$1" %}"#), + ].into_iter() + .map(|(r, s)| (regex::Regex::new(r).unwrap(), s)) + .collect(); + } + let content = REPLACEMENTS_REF + .iter() + .fold(content, |content, &(ref search, ref replace)| { + search.replace_all(&content, *replace).into_owned() + }); + Ok(content) +} + +fn migrate_includes(config: cobalt::Config) -> Result<()> { + let layouts_dir = config.source.join(config.layouts_dir); + let includes_dir = config.source.join(config.includes_dir); + info!("Migrating (potential) snippets to {:?}", includes_dir); + + let files = cobalt::files::FilesBuilder::new(&layouts_dir)? + .ignore_hidden(false)? + .build()?; + for file in files.files() { + let rel_src = file.strip_prefix(&layouts_dir) + .expect("file was found under the root"); + let dest = includes_dir.join(rel_src); + cobalt::files::copy_file(&file, &dest)?; + } + + let template_extensions: Vec<&ffi::OsStr> = config + .template_extensions + .iter() + .map(ffi::OsStr::new) + .collect(); + + // HACK: Assuming its safe to run this conversion on everything + let files = cobalt::files::FilesBuilder::new(&config.source)? + .ignore_hidden(false)? + .build()?; + for file in files.files().filter(|p| { + template_extensions.contains(&p.extension().unwrap_or_else(|| ffi::OsStr::new(""))) + }) { + let content = cobalt::files::read_file(&file)?; + let content = migrate_includes_path(content)?; + cobalt::files::write_document_file(content, file)?; + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn migrade_includes_path_ok() { + let fixture = r#"{{ include "_layouts/_head.liquid" }}"#.to_owned(); + let expected = r#"{% include "_head.liquid" %}"#.to_owned(); + let actual = migrate_includes_path(fixture).unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn migrade_includes_path_complex() { + let fixture = + r#"Hi {{ include "_layouts/head.liquid" }} my {{ include "_layouts/foot.liquid" }}" du"# + .to_owned(); + let expected = r#"Hi {% include "head.liquid" %} my {% include "foot.liquid" %}" du"# + .to_owned(); + let actual = migrate_includes_path(fixture).unwrap(); + assert_eq!(expected, actual); + } +}