1+ import fs from "fs"
2+ import path from "path"
3+ import dotenv from "dotenv"
4+
5+ class Configuration {
6+ static load ( ) {
7+ dotenv . config ( )
8+ }
9+
10+ static get ( key ) {
11+ return process . env [ key ]
12+ }
13+ }
14+
15+ class Logger {
16+ static isDevelopment = Configuration . get ( "TOOLS_ENV" ) === "development"
17+
18+ static Types = Object . freeze ( {
19+ INFO : "Info" ,
20+ WARN : "Warning" ,
21+ ERROR : "Error" ,
22+ EMPTY : ""
23+ } )
24+
25+ static log ( text , type = Logger . Types . EMPTY ) {
26+ const typeFormatted = type ? `${ type } :` : ""
27+ if ( this . isDevelopment ) console . log ( `${ typeFormatted } ` , text )
28+ }
29+ }
30+
31+ class FlagsProcessor {
32+ static formatFlagsString ( args ) {
33+ if ( ! Array . isArray ( args ) || args . length === 0 ) return ""
34+
35+ return args . map ( ( token , index ) =>
36+ index === args . length - 1 ? token : index % 2 === 0 ? `${ token } ` : `${ token } +`
37+ ) . join ( "" )
38+ }
39+
40+ static parseFlags ( flags ) {
41+ const paramWithValue = flags . split ( "+" )
42+ const flagsObject = { }
43+ paramWithValue . forEach ( token => {
44+ const [ attr , value ] = token . split ( "" )
45+ flagsObject [ attr ] = value
46+ } )
47+ return flagsObject
48+ }
49+ }
50+
51+ class FileHandler {
52+ static ActionTypes = Object . freeze ( { MAKE : "make" , DELETE : "delete" , REMOVE : "remove" } )
53+ static EntityTypes = Object . freeze ( { BLOG : "blog" , PROJECT : "project" , PROTOTYPE : "prototype" } )
54+
55+ static generateFilePath ( title , type , flags ) {
56+ let date = flags . d || new Date ( ) . toISOString ( )
57+ const [ year , month , day ] = date . split ( "T" ) [ 0 ] . split ( "-" )
58+
59+ if ( ! ( year && month && day ) ) {
60+ console . log ( "Invalid date format. Please provide -d yyyy-mm-dd" )
61+ process . exit ( 1 )
62+ }
63+
64+ const formattedDate = type === this . EntityTypes . PROTOTYPE ? year : `${ year } -${ month } -${ day } `
65+ const filename = `${ formattedDate } -${ title . replace ( / \s + / g, "-" ) . toLowerCase ( ) } .md` . replace ( / [ < > : " \/ \\ | ? * ] + / g, "" )
66+ const filepath = path . join ( process . cwd ( ) , `src/articles/${ type } ` , filename )
67+
68+ return { filename, filepath }
69+ }
70+
71+ static handleError ( error , filename , action = this . ActionTypes . MAKE ) {
72+ if ( error ) return console . error ( `Error creating file: ${ error . message } ` )
73+ console . log ( `File "${ filename } " ${ action === this . ActionTypes . MAKE ? "created" : "removed" } successfully.` )
74+ }
75+
76+ static generateFile ( title , type , flags , getContentFunction ) {
77+ const { filename, filepath } = this . generateFilePath ( title , type , flags )
78+ const content = getContentFunction ( title , flags )
79+ fs . writeFile ( filepath , content , ( error ) => this . handleError ( error , filename ) )
80+ }
81+
82+ static removeFile ( title , type , flags ) {
83+ const { filename, filepath } = this . generateFilePath ( title , type , flags )
84+ fs . rm ( filepath , ( error ) => this . handleError ( error , filename , this . ActionTypes . DELETE ) )
85+ }
86+
87+ static getAllActionTypes ( ) {
88+ return Object . values ( this . ActionTypes ) . map ( ( action , index , arr ) => ( index === arr . length - 1 ? ` and \`${ action } \`` : ` \`${ action } \`,` ) ) . join ( "" )
89+ }
90+
91+ static getAllEntityTypes ( ) {
92+ return Object . values ( this . EntityTypes ) . map ( ( entity , index , arr ) => ( index === arr . length - 1 ? ` and \`${ entity } \`` : ` \`${ entity } \`,` ) ) . join ( "" )
93+ }
94+ }
95+
96+ class ContentGenerator {
97+ static getDefaultBlogContent ( title , flags = { } ) {
98+ return `---
99+ title: ${ title }
100+ description:
101+ author:
102+ draft: true
103+ date: ${ new Date ( ) . toISOString ( ) }
104+ tags:
105+ - post
106+ ${ typeof flags . t === "string" ? flags . t . split ( "," ) . map ( tag => ` - ${ tag } ` ) . join ( "\n" ) : "" }
107+ ---\n`
108+ }
109+
110+ static getDefaultProjectContent ( title , flags = { } ) {
111+ return `---
112+ title: ${ title }
113+ description:
114+ date:
115+ endDate:
116+ image:
117+ linkDemo:
118+ linkCode:
119+ tags:
120+ - project
121+ ${ typeof flags . t === "string" ? flags . t . split ( "," ) . map ( tag => ` - ${ tag } ` ) . join ( "\n" ) : "" }
122+ ---\n`
123+ }
124+
125+ static getDefaultPrototypeContent ( title , flags = { } ) {
126+ return `---
127+ title: ${ title }
128+ status: 1
129+ description:
130+ date:
131+ linkDemo:
132+ language:
133+ code: |-
134+ tags:
135+ - prototype
136+ ${ typeof flags . t === "string" ? flags . t . split ( "," ) . map ( tag => ` - ${ tag } ` ) . join ( "\n" ) : "" }
137+ ---\n`
138+ }
139+ }
140+
141+ class Ewtools {
142+ constructor ( args ) {
143+ this . args = args
144+ this . command = args [ 0 ]
145+ this . title = args [ 1 ]
146+ this . restArgs = args . slice ( 2 )
147+ this . flags = FlagsProcessor . parseFlags ( FlagsProcessor . formatFlagsString ( this . restArgs ) )
148+ this . action = this . command . split ( ":" ) [ 0 ]
149+ this . type = this . command . split ( ":" ) [ 1 ]
150+ }
151+
152+ validateArgs ( ) {
153+ if ( this . args . length < 2 ) {
154+ console . log ( "Usage: ./ewtools.bat make:blog \"title\"" )
155+ process . exit ( 1 )
156+ }
157+ }
158+
159+ execute ( ) {
160+ this . validateArgs ( )
161+ Logger . log ( this . args , Logger . Types . INFO )
162+ Logger . log ( this . flags , Logger . Types . INFO )
163+
164+ switch ( this . action ) {
165+ case FileHandler . ActionTypes . MAKE :
166+ this . handleMake ( )
167+ break
168+
169+ case FileHandler . ActionTypes . REMOVE :
170+ case FileHandler . ActionTypes . DELETE :
171+ this . handleDelete ( )
172+ break
173+
174+ default :
175+ console . log ( `Invalid Action. Valid Actions: ${ FileHandler . getAllActionTypes ( ) } ` )
176+ }
177+ }
178+
179+ handleMake ( ) {
180+ switch ( this . type ) {
181+ case FileHandler . EntityTypes . BLOG :
182+ FileHandler . generateFile ( this . title , this . type , this . flags , ContentGenerator . getDefaultBlogContent )
183+ break
184+
185+ case FileHandler . EntityTypes . PROJECT :
186+ FileHandler . generateFile ( this . title , this . type , this . flags , ContentGenerator . getDefaultProjectContent )
187+ break
188+
189+ case FileHandler . EntityTypes . PROTOTYPE :
190+ FileHandler . generateFile ( this . title , this . type , this . flags , ContentGenerator . getDefaultPrototypeContent )
191+ break
192+
193+ default :
194+ console . log ( `Invalid Type, Valid Types: ${ FileHandler . getAllEntityTypes ( ) } ` )
195+ }
196+ }
197+
198+ handleDelete ( ) {
199+ if ( this . type === "" || this . type === undefined ) return console . log ( `Invalid Type, Valid Types: ${ FileHandler . getAllEntityTypes ( ) } ` )
200+ FileHandler . removeFile ( this . title , this . type , this . flags )
201+ }
202+ }
203+
204+ Configuration . load ( )
205+ const args = process . argv . slice ( 2 )
206+
207+ if ( args . length <= 0 ) {
208+ console . log ( "Usage: ./ewtools.bat make:blog \"title\"" )
209+ } else {
210+ new Ewtools ( args ) . execute ( )
211+ }
0 commit comments