diff --git a/Cargo.lock b/Cargo.lock index 29140a2..1d69eeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,6 +137,12 @@ dependencies = [ "quote", ] +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + [[package]] name = "memchr" version = "2.7.1" @@ -190,6 +196,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + [[package]] name = "serde" version = "1.0.195" @@ -210,6 +222,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "strsim" version = "0.10.0" @@ -229,13 +252,15 @@ dependencies = [ [[package]] name = "torytis" -version = "1.0.0-test.4" +version = "1.0.0-test.5" dependencies = [ "clap", "glob", "include_dir", "regex", "serde", + "serde_json", + "version-compare", "xmltree", ] @@ -251,6 +276,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 8ac177f..9235628 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "torytis" -version = "1.0.0-test.4" +version = "1.0.0-test.5" edition = "2021" [dependencies] @@ -9,4 +9,6 @@ glob = "0.3.1" include_dir = { version = "0.7.3", features = ["glob"] } regex = "1.10.2" serde = { version = "1.0.195", features = ["derive"] } +serde_json = "1.0.111" +version-compare = "0.1.1" xmltree = "0.10.3" diff --git a/package-lock.json b/package-lock.json index 9c1b448..fccfd3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@wisdomstar94/torytis", - "version": "1.0.0-test.4", + "version": "1.0.0-test.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@wisdomstar94/torytis", - "version": "1.0.0-test.4", + "version": "1.0.0-test.5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 4c61f3b..1c86e92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@wisdomstar94/torytis", - "version": "1.0.0-test.4", + "version": "1.0.0-test.5", "description": "티스토리 블로그 스킨 개발 프레임워크", "main": "index.js", "scripts": { diff --git a/src/lib.rs b/src/lib.rs index 4bf40bc..a04aa7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use std::process::Stdio; use clap::Parser; use regex::Regex; mod sub_commands; +mod statics; #[derive(Parser)] // requires `derive` feature #[command(name = "torytis")] @@ -13,6 +14,7 @@ enum Cli { New(sub_commands::c_new::CliArgs), Build(sub_commands::c_build::CliArgs), Varbuild(sub_commands::c_varbuild::CliArgs), + Migrate(sub_commands::c_migrate::CliArgs), } pub fn run() { @@ -21,6 +23,7 @@ pub fn run() { Cli::New(args) => sub_commands::c_new::run(args), Cli::Build(args) => sub_commands::c_build::run(args), Cli::Varbuild(args) => sub_commands::c_varbuild::run(args), + Cli::Migrate(args) => sub_commands::c_migrate::run(args), } } diff --git a/src/statics.rs b/src/statics.rs new file mode 100644 index 0000000..e61f93f --- /dev/null +++ b/src/statics.rs @@ -0,0 +1,5 @@ +use include_dir::{include_dir, Dir}; + +pub static STATIC_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static"); +pub static PROJECT_TEMPLATE_NAME: &str = "project-template"; +pub static VERSION: &str = env!("CARGO_PKG_VERSION"); \ No newline at end of file diff --git a/src/sub_commands.rs b/src/sub_commands.rs index 898fd7c..37337c1 100644 --- a/src/sub_commands.rs +++ b/src/sub_commands.rs @@ -1,3 +1,4 @@ pub mod c_new; pub mod c_build; -pub mod c_varbuild; \ No newline at end of file +pub mod c_varbuild; +pub mod c_migrate; \ No newline at end of file diff --git a/src/sub_commands/c_build.rs b/src/sub_commands/c_build.rs index 01184ae..c020080 100644 --- a/src/sub_commands/c_build.rs +++ b/src/sub_commands/c_build.rs @@ -1,5 +1,6 @@ -use std::{env, fs::{self, DirEntry}, io}; +use std::{env, fs::{self, DirEntry}, io, collections::HashMap}; use glob::glob; +use serde_json::Value; use crate::{run_command, replace_skin_html_content}; #[derive(clap::Args)] @@ -16,6 +17,15 @@ pub fn run(_: CliArgs) { let working_dir_path_buf = env::current_dir().unwrap(); let torytis_build_ts_file_path_buf = working_dir_path_buf.join("torytis-build.tsx"); let torytis_build_js_file_path_buf = working_dir_path_buf.join("torytis-build.js"); + let dot_torytis_index_xml_path_buf = working_dir_path_buf.join(".torytis").join("index.xml"); + let dot_torytis_index_xml_path = dot_torytis_index_xml_path_buf.as_path(); + let src_public_index_xml_path_buf = working_dir_path_buf.join("src").join("public").join("index.xml"); + let src_public_index_xml_path = src_public_index_xml_path_buf.as_path(); + + // src/public/index.xml 체크 + if let Err(_) = fs::metadata(src_public_index_xml_path) { + panic!("src/public/index.xml 파일이 존재하지 않습니다. 해당 파일을 생성 후 다시 시도해주세요. (https://tistory.github.io/document-tistory-skin/common/index.xml.html)") + } // .torytis 폴더 체크 및 생성 let dot_torytis_dir_path_buf = working_dir_path_buf.join(".torytis/"); @@ -71,7 +81,7 @@ pub fn run(_: CliArgs) { // } let torytis_dot_index_css_file_path_buf = dot_torytis_dir_path_buf.join("index.css"); let torytis_dot_style_css_file_path_buf = dot_torytis_dir_path_buf.join("style.css"); - let tailwind_build_command = format!("npm run tailwindcss -- -c ./tailwind.config.js -i {} -o {}", torytis_dot_index_css_file_path_buf.to_str().unwrap(), torytis_dot_style_css_file_path_buf.to_str().unwrap()); + let tailwind_build_command = format!("npm run tailwindcss -- -c ./tailwind.config.ts -i {} -o {}", torytis_dot_index_css_file_path_buf.to_str().unwrap(), torytis_dot_style_css_file_path_buf.to_str().unwrap()); println!("> {}", tailwind_build_command); { let _ = run_command(tailwind_build_command.as_str()).unwrap(); @@ -102,4 +112,19 @@ pub fn run(_: CliArgs) { let after_path_bug = dot_torytis_dir_path_buf.join(entry.file_name()); fs::copy(entry_path_buf.as_path(), after_path_bug).unwrap(); } + + // .torytis/index.xml 에서 문자 치환하기 + let dot_torytis_index_xml_content = fs::read_to_string(dot_torytis_index_xml_path).unwrap(); + let mut dot_torytis_index_xml_content_new = dot_torytis_index_xml_content.clone(); + + let package_json_path_buf = working_dir_path_buf.join("package.json"); + let package_json_path = package_json_path_buf.as_path(); + let package_json_string = fs::read_to_string(package_json_path).unwrap(); + let package_json = serde_json::from_str::>(&package_json_string); + if let Ok(result) = package_json { + if let Some(version) = result.get("version") { + dot_torytis_index_xml_content_new = dot_torytis_index_xml_content_new.replace("{ version }", version.as_str().unwrap()); + fs::write(dot_torytis_index_xml_path, &dot_torytis_index_xml_content_new).unwrap(); + } + } } \ No newline at end of file diff --git a/src/sub_commands/c_migrate.rs b/src/sub_commands/c_migrate.rs new file mode 100644 index 0000000..f0754f7 --- /dev/null +++ b/src/sub_commands/c_migrate.rs @@ -0,0 +1,137 @@ +use std::{env, fs::{self}, collections::HashMap}; +use crate::statics::{STATIC_DIR, VERSION}; +use regex::Regex; +use serde_json::Value; +use version_compare::Version; + +#[derive(clap::Args)] +#[command( + about="torytis 프로젝트를 마이그레이션합니다.", + long_about = None) +] +pub struct CliArgs { + // #[arg(short='n', long="name")] + // name: Option, +} + +pub fn run(_: CliArgs) { + let working_dir_path_buf = env::current_dir().unwrap(); + + // ## package.json 파일 체크 + let package_json_file_path_buf = working_dir_path_buf.join("package.json"); + let package_json_file_path = package_json_file_path_buf.as_path(); + if let Err(_) = fs::metadata(package_json_file_path) { + panic!("-> npm 프로젝트 폴더가 아닙니다."); + } + + let package_json_content = fs::read_to_string(package_json_file_path).unwrap(); + let mut package_json_content_mut = package_json_content.clone(); + // println!("-> package_json_content : {:#?}", package_json_content); + let json_parse_result = serde_json::from_str::>(&package_json_content_mut); + if let Err(err) = json_parse_result { + panic!("-> package.json 내용에 오류가 있습니다. : {:#?}", err); + } + + println!("-> migration start!"); + println!("-> current version : {:#?}", VERSION); + + // v0 -> v1 + if Version::from("1.0.0").unwrap() <= Version::from(VERSION).unwrap() { + // if true { + // torytis-build.tsx 파일 체크 + let torytis_build_tsx_file_path_buf = working_dir_path_buf.join("torytis-build.tsx"); + let torytis_build_tsx_file_path = torytis_build_tsx_file_path_buf.as_path(); + if let Err(_) = fs::metadata(torytis_build_tsx_file_path) { + let file = STATIC_DIR.get_file("project-template/torytis-build.tsx").unwrap(); + let file_content = file.contents_utf8().unwrap(); + fs::write(torytis_build_tsx_file_path, file_content).unwrap(); + } + // package.json 파일 체크 + let scripts_block_string = get_scripts_block_string_from_package_json_content(&package_json_content_mut); + if let Some(string) = scripts_block_string { + // scripts 블록이 있는 경우 + let mut scripts_block_string_new = String::from(string); + let pattern = r#"\"scripts\":[^{}]*\{"#; + let regex = Regex::new(&pattern).unwrap(); + + if !scripts_block_string_new.contains("\"tsc\":") { + let mut insert_string = String::from(""); + insert_string.push_str("\"scripts\": {\n"); + insert_string.push_str("\t\t\"tsc\": \"tsc\","); + scripts_block_string_new = regex.replace(&scripts_block_string_new, insert_string).to_string(); + } + + if !scripts_block_string_new.contains("\"tailwindcss\":") { + let mut insert_string = String::from(""); + insert_string.push_str("\"scripts\": {\n"); + insert_string.push_str("\t\t\"tailwindcss\": \"tailwindcss\","); + scripts_block_string_new = regex.replace(&scripts_block_string_new, insert_string).to_string(); + } + + package_json_content_mut = get_scripts_block_regex().replace(&package_json_content_mut, &scripts_block_string_new).to_string(); + } else { + // scripts 블록이 없는 경우 + panic!("package.json 에 \"scripts\" 가 선언되어 있지 않습니다. 선언 후에 다시 시도해주세요."); + } + + let dev_dependencies_block_string = get_dev_dependencies_block_string_from_package_json_content(&package_json_content); + if let Some(string) = dev_dependencies_block_string { + // devDependencies 블록이 있는 경우 + let mut dev_dependencies_block_string_new = String::from(string); + let pattern = r#"\"devDependencies\":[^{}]*\{"#; + let regex = Regex::new(&pattern).unwrap(); + + if !dev_dependencies_block_string_new.contains("\"esbuild-sass-plugin\":") { + let mut insert_string = String::from(""); + insert_string.push_str("\"devDependencies\": {\n"); + insert_string.push_str("\t\t\"esbuild-sass-plugin\": \"^2\","); + dev_dependencies_block_string_new = regex.replace(&dev_dependencies_block_string_new, insert_string).to_string(); + } + + package_json_content_mut = get_dev_dependencies_regex().replace(&package_json_content_mut, &dev_dependencies_block_string_new).to_string(); + } else { + // devDependencies 블록이 없는 경우 + panic!("package.json 에 \"devDependencies\" 가 선언되어 있지 않습니다. 선언 후에 다시 시도해주세요."); + } + + fs::write(package_json_file_path, &package_json_content_mut).unwrap(); + } + + println!("-> migration end!"); +} + +fn get_scripts_block_regex() -> Regex { + let pattern = r#""scripts":[^{}]*\{[^{}]*\}"#; + let regex = Regex::new(&pattern).unwrap(); + regex +} + +fn get_dev_dependencies_regex() -> Regex { + let pattern = r#""devDependencies":[^{}]*\{[^{}]*\}"#; + let regex = Regex::new(&pattern).unwrap(); + regex +} + +fn get_scripts_block_string_from_package_json_content(input: &str) -> Option { + let mut result: Option = None; + if let Some(captured) = get_scripts_block_regex().captures(input) { + // 첫 번째 캡처된 그룹 출력 + if let Some(script_block) = captured.get(0) { + // println!("{}", script_block.as_str()); + result = Some(script_block.as_str().to_string()); + } + } + result +} + +fn get_dev_dependencies_block_string_from_package_json_content(input: &str) -> Option { + let mut result: Option = None; + if let Some(captured) = get_dev_dependencies_regex().captures(input) { + // 첫 번째 캡처된 그룹 출력 + if let Some(script_block) = captured.get(0) { + // println!("{}", script_block.as_str()); + result = Some(script_block.as_str().to_string()); + } + } + result +} diff --git a/src/sub_commands/c_new.rs b/src/sub_commands/c_new.rs index c329a91..2f00090 100644 --- a/src/sub_commands/c_new.rs +++ b/src/sub_commands/c_new.rs @@ -1,10 +1,5 @@ use std::{env, path::Path, fs}; -use include_dir::{include_dir, Dir}; - -use crate::run_command; - -static STATIC_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static"); -static PROJECT_TEMPLATE_NAME: &str = "project-template"; +use crate::{run_command, statics::{PROJECT_TEMPLATE_NAME, STATIC_DIR}}; #[derive(clap::Args)] #[command( diff --git a/src/sub_commands/c_varbuild.rs b/src/sub_commands/c_varbuild.rs index 0f0a767..983c845 100644 --- a/src/sub_commands/c_varbuild.rs +++ b/src/sub_commands/c_varbuild.rs @@ -1,9 +1,8 @@ use std::{env, fs::{self}}; -use include_dir::{include_dir, Dir}; use regex::Regex; use xmltree::Element; -static STATIC_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static"); +use crate::statics::STATIC_DIR; #[derive(clap::Args)] #[command( diff --git a/static/project-template/tailwind.config.js b/static/project-template/tailwind.config.js deleted file mode 100644 index cc505a5..0000000 --- a/static/project-template/tailwind.config.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - // ... - './.torytis/index.css', - './src/**/*.{ts,tsx,css,scss}', - ], - // ... -}; diff --git a/static/project-template/tailwind.config.ts b/static/project-template/tailwind.config.ts new file mode 100644 index 0000000..e0b56d0 --- /dev/null +++ b/static/project-template/tailwind.config.ts @@ -0,0 +1,12 @@ +import type { Config } from 'tailwindcss'; + +export default { + content: [ + './.torytis/index.css', + './src/**/*.{ts,tsx,css,scss}', + ], + theme: { + extend: {}, + }, + plugins: [], + } satisfies Config \ No newline at end of file diff --git a/tests/compare_version_test.rs b/tests/compare_version_test.rs new file mode 100644 index 0000000..366ce57 --- /dev/null +++ b/tests/compare_version_test.rs @@ -0,0 +1,7 @@ +use version_compare::compare; + +#[test] +fn compare_version_test() { + let k = compare("1.0.0", "1.0.0").unwrap(); + println!("k : {:?}", k); +} \ No newline at end of file