1+ import path from "node:path" ;
2+ import fs from 'node:fs/promises' ;
3+ import { URL } from 'url' ;
4+ import process from "node:process" ;
5+
6+ const __dirname = new URL ( '.' , import . meta. url ) . pathname ;
7+
8+ const PATH_DEMO_SRC = path . join ( __dirname , '..' , 'src' ) ;
9+ const PATH_LIB_SRC = path . join ( __dirname , ".." , ".." , ".." , "packages" , "react-tools" , "src" ) ;
10+ const HOOKS_DIR_NAME = "hooks" ;
11+ const COMPONENTS_DIR_NAME = "components" ;
12+ const UTILS_DIR_NAME = "utils" ;
13+ const HOOKS_TYPE = [ "state" , "lifecycle" , "performance" , "events" , "api-dom" ] ;
14+
15+ /**
16+ *
17+ * @param {{readonly value: string;add(s: string): this;set(s: string[]): this;} } router
18+ */
19+ async function generateImport ( router ) {
20+ router . set ( [
21+ "import { Link, useLocation, useOutlet } from 'react-router-dom';" ,
22+ "import Logo from '../assets/github.svg';" ,
23+ "import React from '../assets/react-red.webp';" ,
24+ "import { useCallback, useRef } from 'react';" ,
25+ "import { CSSTransition, SwitchTransition } from 'react-transition-group'"
26+ ] ) ;
27+ }
28+
29+ /**
30+ *
31+ * @param {{readonly value: string;add(s: string): this;set(s: string[]): this;} } router
32+ * @param {string[] } stateFiles
33+ * @param {string[] } lifecycleFiles
34+ * @param {string[] } performanceFiles
35+ * @param {string[] } eventsfiles
36+ * @param {string[] } apiDomFiles
37+ */
38+ function createLinkHooksRoutes ( router , stateFiles , lifecycleFiles , performanceFiles , eventsFiles , apiDomFiles , ) {
39+ router . add ( ' <p className="sub-type">State</p>' ) ;
40+ stateFiles . forEach ( f => {
41+ const [ name ] = f . split ( "." ) ;
42+ router . add ( ' <Link' ) ;
43+ router . add ( ` className={pathname === "/hooks/state/${ name } " ? 'active' : ''}` ) ;
44+ router . add ( ` to="/hooks/state/${ name } "` ) ;
45+ router . add ( ' onClick={() => {' ) ;
46+ router . add ( ' containerRef.current?.scrollTo(0, 0);' ) ;
47+ router . add ( ' window.innerWidth < 1190 && closeNav();' ) ;
48+ router . add ( ' }}' ) ;
49+ router . add ( ' >' ) ;
50+ router . add ( ` ${ name } ` ) ;
51+ router . add ( ' </Link>' ) ;
52+ } ) ;
53+ router . add ( ' <p className="sub-type">Lifecycle</p>' ) ;
54+ lifecycleFiles . forEach ( f => {
55+ const [ name ] = f . split ( "." ) ;
56+ router . add ( ' <Link' ) ;
57+ router . add ( ` className={pathname === "/hooks/lifecycle/${ name } " ? 'active' : ''}` ) ;
58+ router . add ( ` to="/hooks/lifecycle/${ name } "` ) ;
59+ router . add ( ' onClick={() => {' ) ;
60+ router . add ( ' containerRef.current?.scrollTo(0, 0);' ) ;
61+ router . add ( ' window.innerWidth < 1190 && closeNav();' ) ;
62+ router . add ( ' }}' ) ;
63+ router . add ( ' >' ) ;
64+ router . add ( ` ${ name } ` ) ;
65+ router . add ( ' </Link>' ) ;
66+ } ) ;
67+ router . add ( ' <p className="sub-type">Performance</p>' ) ;
68+ performanceFiles . forEach ( f => {
69+ const [ name ] = f . split ( "." ) ;
70+ router . add ( ' <Link' ) ;
71+ router . add ( ` className={pathname === "/hooks/performance/${ name } " ? 'active' : ''}` ) ;
72+ router . add ( ` to="/hooks/performance/${ name } "` ) ;
73+ router . add ( ' onClick={() => {' ) ;
74+ router . add ( ' containerRef.current?.scrollTo(0, 0);' ) ;
75+ router . add ( ' window.innerWidth < 1190 && closeNav();' ) ;
76+ router . add ( ' }}' ) ;
77+ router . add ( ' >' ) ;
78+ router . add ( ` ${ name } ` ) ;
79+ router . add ( ' </Link>' ) ;
80+ } ) ;
81+ router . add ( ' <p className="sub-type">Events</p>' ) ;
82+ eventsFiles . forEach ( f => {
83+ const [ name ] = f . split ( "." ) ;
84+ router . add ( ' <Link' ) ;
85+ router . add ( ` className={pathname === "/hooks/events/${ name } " ? 'active' : ''}` ) ;
86+ router . add ( ` to="/hooks/events/${ name } "` ) ;
87+ router . add ( ' onClick={() => {' ) ;
88+ router . add ( ' containerRef.current?.scrollTo(0, 0);' ) ;
89+ router . add ( ' window.innerWidth < 1190 && closeNav();' ) ;
90+ router . add ( ' }}' ) ;
91+ router . add ( ' >' ) ;
92+ router . add ( ` ${ name } ` ) ;
93+ router . add ( ' </Link>' ) ;
94+ } ) ;
95+ router . add ( ' <p className="sub-type">API DOM</p>' ) ;
96+ apiDomFiles . forEach ( f => {
97+ const [ name ] = f . split ( "." ) ;
98+ router . add ( ' <Link' ) ;
99+ router . add ( ` className={pathname === "/hooks/api-${ name } " ? 'active' : ''}` ) ;
100+ router . add ( ` to="/hooks/api-dom/${ name } "` ) ;
101+ router . add ( ' onClick={() => {' ) ;
102+ router . add ( ' containerRef.current?.scrollTo(0, 0);' ) ;
103+ router . add ( ' window.innerWidth < 1190 && closeNav();' ) ;
104+ router . add ( ' }}' ) ;
105+ router . add ( ' >' ) ;
106+ router . add ( ` ${ name } ` ) ;
107+ router . add ( ' </Link>' ) ;
108+ } ) ;
109+ }
110+
111+ /**
112+ *
113+ * @param {{readonly value: string;add(s: string): this;set(s: string[]): this;} } router
114+ * @param {string[] } componentsFiles
115+ * @param {string } parentRoot
116+ */
117+ function createLinkRoutes ( router , componentsFiles , parentRoot ) {
118+ router . add ( ` <p className="sub-type">${ parentRoot . charAt ( 0 ) . toUpperCase ( ) + parentRoot . substring ( 1 ) } </p>` ) ;
119+ componentsFiles . forEach ( f => {
120+ const [ name ] = f . split ( "." ) ;
121+ router . add ( ' <Link' ) ;
122+ router . add ( ` className={pathname === "/${ parentRoot } /${ name } " ? 'active' : ''}` ) ;
123+ router . add ( ` to="/${ parentRoot } /${ name } "` ) ;
124+ router . add ( ' onClick={() => {' ) ;
125+ router . add ( ' containerRef.current?.scrollTo(0, 0);' ) ;
126+ router . add ( ' window.innerWidth < 1190 && closeNav();' ) ;
127+ router . add ( ' }}' ) ;
128+ router . add ( ' >' ) ;
129+ router . add ( ` ${ name } ` ) ;
130+ router . add ( ' </Link>' ) ;
131+ } ) ;
132+ }
133+
134+ /**
135+ *
136+ * @param {{readonly value: string;add(s: string): this;set(s: string[]): this;} } router
137+ */
138+ async function createLinkRouter ( router ) {
139+ const libSrcIndexFile = await fs . readFile ( path . join ( PATH_LIB_SRC , "index.ts" ) ) ;
140+ const HOOKS_STATE_FILES = ( await fs . readFile ( ( path . join ( PATH_LIB_SRC , HOOKS_DIR_NAME , HOOKS_TYPE [ 0 ] , "index.ts" ) ) , { encoding : "utf8" } ) ) . replaceAll ( "export { " , "" ) . replaceAll ( "export " , "" ) . split ( "\n" ) . map ( el => el . substring ( 0 , el . indexOf ( " } from" ) !== - 1 ? el . indexOf ( " } from" ) : el . indexOf ( " from" ) ) ) . filter ( el => ! ! el && libSrcIndexFile . includes ( el ) ) . sort ( ( a , b ) => a . localeCompare ( b , 'en' ) ) ;
141+ const HOOKS_LIFECYCLE_FILES = ( await fs . readFile ( ( path . join ( PATH_LIB_SRC , HOOKS_DIR_NAME , HOOKS_TYPE [ 1 ] , "index.ts" ) ) , { encoding : "utf8" } ) ) . replaceAll ( "export { " , "" ) . replaceAll ( "export " , "" ) . split ( "\n" ) . map ( el => el . substring ( 0 , el . indexOf ( " } from" ) !== - 1 ? el . indexOf ( " } from" ) : el . indexOf ( " from" ) ) ) . filter ( el => ! ! el && libSrcIndexFile . includes ( el ) ) . sort ( ( a , b ) => a . localeCompare ( b , 'en' ) ) ;
142+ const HOOKS_PERFORMANCE_FILES = ( await fs . readFile ( ( path . join ( PATH_LIB_SRC , HOOKS_DIR_NAME , HOOKS_TYPE [ 2 ] , "index.ts" ) ) , { encoding : "utf8" } ) ) . replaceAll ( "export { " , "" ) . replaceAll ( "export " , "" ) . split ( "\n" ) . map ( el => el . substring ( 0 , el . indexOf ( " } from" ) !== - 1 ? el . indexOf ( " } from" ) : el . indexOf ( " from" ) ) ) . filter ( el => ! ! el && libSrcIndexFile . includes ( el ) ) . sort ( ( a , b ) => a . localeCompare ( b , 'en' ) ) ;
143+ const HOOKS_EVENTS_FILES = ( await fs . readFile ( ( path . join ( PATH_LIB_SRC , HOOKS_DIR_NAME , HOOKS_TYPE [ 3 ] , "index.ts" ) ) , { encoding : "utf8" } ) ) . replaceAll ( "export { " , "" ) . replaceAll ( "export " , "" ) . split ( "\n" ) . map ( el => el . substring ( 0 , el . indexOf ( " } from" ) !== - 1 ? el . indexOf ( " } from" ) : el . indexOf ( " from" ) ) ) . filter ( el => ! ! el && libSrcIndexFile . includes ( el ) ) . sort ( ( a , b ) => a . localeCompare ( b , 'en' ) ) ;
144+ const HOOKS_APIDOM_FILES = ( await fs . readFile ( ( path . join ( PATH_LIB_SRC , HOOKS_DIR_NAME , HOOKS_TYPE [ 4 ] , "index.ts" ) ) , { encoding : "utf8" } ) ) . replaceAll ( "export { " , "" ) . replaceAll ( "export " , "" ) . split ( "\n" ) . map ( el => el . substring ( 0 , el . indexOf ( " } from" ) !== - 1 ? el . indexOf ( " } from" ) : el . indexOf ( " from" ) ) ) . filter ( el => ! ! el && libSrcIndexFile . includes ( el ) ) . sort ( ( a , b ) => a . localeCompare ( b , 'en' ) ) ;
145+ const COMPONENTS_FILES = ( await fs . readFile ( ( path . join ( PATH_LIB_SRC , COMPONENTS_DIR_NAME , "index.ts" ) ) , { encoding : "utf8" } ) ) . replaceAll ( "export { " , "" ) . replaceAll ( "export " , "" ) . split ( "\n" ) . map ( el => el . substring ( 0 , el . indexOf ( " } from" ) !== - 1 ? el . indexOf ( " } from" ) : el . indexOf ( " from" ) ) ) . filter ( el => ! ! el && libSrcIndexFile . includes ( el ) ) . sort ( ( a , b ) => a . localeCompare ( b , 'en' ) ) ;
146+ const UTILS_FILES = ( await fs . readFile ( ( path . join ( PATH_LIB_SRC , UTILS_DIR_NAME , "index.ts" ) ) , { encoding : "utf8" } ) ) . replaceAll ( "export { " , "" ) . replaceAll ( "export " , "" ) . split ( "\n" ) . map ( el => el . substring ( 0 , el . indexOf ( " } from" ) !== - 1 ? el . indexOf ( " } from" ) : el . indexOf ( " from" ) ) ) . filter ( el => ! ! el && libSrcIndexFile . includes ( el ) ) . sort ( ( a , b ) => a . localeCompare ( b , 'en' ) ) ;
147+
148+ router . add ( " <p className='type'>Hooks</p>" ) ;
149+ createLinkHooksRoutes ( router , HOOKS_STATE_FILES , HOOKS_LIFECYCLE_FILES , HOOKS_PERFORMANCE_FILES , HOOKS_EVENTS_FILES , HOOKS_APIDOM_FILES ) ;
150+ createLinkRoutes ( router , COMPONENTS_FILES , "components" )
151+ createLinkRoutes ( router , UTILS_FILES , "utils" )
152+ }
153+
154+ async function generateMainLayout ( ) {
155+ try {
156+ const stringBuffer = {
157+ value : "" ,
158+ /**
159+ *
160+ * @param {string } s
161+ * @returns {this }
162+ */
163+ add ( s ) {
164+ this . value += s + "\n" ;
165+ return this ;
166+ } ,
167+ /**
168+ *
169+ * @param {string[] } s
170+ * @returns {this }
171+ */
172+ set ( s ) {
173+ s . forEach ( slice => { this . value += slice + "\n" } ) ;
174+ return this ;
175+ }
176+ }
177+ await generateImport ( stringBuffer ) ;
178+ stringBuffer . set ( [
179+ "export default function MainLayout() {" ,
180+ " const { pathname } = useLocation();" ,
181+ " const location = useLocation()" ,
182+ " const currentOutlet = useOutlet()" ,
183+ " const nodeRef = useRef<HTMLDivElement>(null);" ,
184+ " const navRef = useRef<HTMLUnknownElement>(null);" ,
185+ " const containerRef = useRef<HTMLDivElement>(null);" ,
186+ " const openNav = useCallback(() => {" ,
187+ ' navRef.current && (navRef.current.style.width = "100%");' ,
188+ " }, []);" ,
189+ " const closeNav = useCallback(() => {" ,
190+ ' navRef.current && (navRef.current.style.width= "0");' ,
191+ " }, []);" ,
192+ ' return (' ,
193+ ' <>' ,
194+ ' {' ,
195+ ' !["/", ""].includes(pathname) &&' ,
196+ ' <>' ,
197+ ' <button onClick={openNav} className="open-nav">☰</button>' ,
198+ ' <nav ref={navRef} className="nav">' ,
199+ ' <button onClick={closeNav} className="btn-close">X</button>' ,
200+ ' <div className="title-container">' ,
201+ ' <Link to="/" className="title">' ,
202+ ' <img src={React} alt="react" className="img" />' ,
203+ ' <p className="text">React Tools</p>' ,
204+ ' </Link>' ,
205+ ' <Link to="https://github.com/nDriaDev/react-tools">' ,
206+ ' <img src={Logo} className="img" alt="github" />' ,
207+ ' </Link>' ,
208+ ' </div>' ,
209+ ] ) ;
210+ await createLinkRouter ( stringBuffer ) ;
211+ stringBuffer . set ( [
212+ ' </nav>' ,
213+ ' </>' ,
214+ ' }' ,
215+ ' <div className="container" ref={containerRef}>' ,
216+ ' <SwitchTransition>' ,
217+ ' <CSSTransition' ,
218+ ' key={location.pathname}' ,
219+ ' nodeRef={nodeRef}' ,
220+ ' timeout={300}' ,
221+ ' classNames="page"' ,
222+ ' unmountOnExit' ,
223+ ' >' ,
224+ ' {() => (' ,
225+ ' <div ref={nodeRef} className="page">' ,
226+ ' {currentOutlet}' ,
227+ ' </div>' ,
228+ ' )}' ,
229+ ' </CSSTransition>' ,
230+ ' </SwitchTransition>' ,
231+ ' </div>' ,
232+ ' </>' ,
233+ ' )' ,
234+ '}'
235+ ] ) ;
236+ await fs . writeFile ( path . join ( PATH_DEMO_SRC , "layout" , "MainLayout.tsx" ) , stringBuffer . value , { encoding : "utf8" } ) ;
237+ process . exit ( 0 ) ;
238+ } catch ( error ) {
239+ console . error ( error ) ;
240+ }
241+ }
242+
243+ generateMainLayout ( ) ;
0 commit comments