Skip to content

Commit 92020cd

Browse files
rootroot
authored andcommitted
[WIP] rework project structure
1 parent d957c48 commit 92020cd

112 files changed

Lines changed: 3193 additions & 297 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/react-tools-demo/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
"version": "0.0.0",
55
"type": "module",
66
"scripts": {
7-
"dev": "node scripts/generateMarkdown.js && vite",
8-
"dev:no-docs": "vite",
9-
"build": "node scripts/generateMarkdown.js && tsc && vite build",
7+
"dev": "node scripts/generateMarkdown.js && node scripts/generateRouter.js && node scripts/generateMainLayout.js && vite",
8+
"dev:no-docs": "node scripts/generateRouter.js && node scripts/generateMainLayout.js && vite",
9+
"build": "node scripts/generateMarkdown.js && node scripts/generateRouter.js && node scripts/generateMainLayout.js && tsc && vite build",
1010
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
1111
"preview": "vite preview"
1212
},
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
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();

apps/react-tools-demo/scripts/generateMarkdown.js

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import process from "node:process";
66

77
const __dirname = new URL('.', import.meta.url).pathname;
88

9-
const pathMarkdownDir = path.join(__dirname, '..', 'src', 'markdown');
10-
const pathHooksDemoDir = path.join(__dirname, '..', 'src', 'pages');
11-
const pathUtilsDir = path.join(__dirname, "..", "..", "..", "packages", "react-tools", "src", "utils");
12-
const pathHooksDir = path.join(__dirname, "..", "..", "..", "packages", "react-tools", "src", "hooks");
9+
const PATH_DEMO_SRC = path.join(__dirname, '..', 'src');
10+
const PATH_LIB_SRC = path.join(__dirname, "..", "..", "..", "packages", "react-tools", "src");
11+
const DEMO_COMPONENT_DIR_NAME = "pages";
12+
const MARKDOWWN_DIR_NAME = "markdown";
13+
const HOOKS_DIR_NAME = "hooks";
14+
const COMPONENTS_DIR_NAME = "components";
15+
const UTILS_DIR_NAME = "utils";
1316

1417
/**
1518
*
@@ -385,48 +388,75 @@ ${lines.length > 0 ? lines.map(line => "> "+line).join("\n") : ""}
385388
}
386389

387390
async function generateUtilsMarkDown() {
388-
const utilsFiles = await fs.readdir(path.join(pathUtilsDir));
389-
const indexFile = await fs.readFile(path.join(pathUtilsDir, "..", "index.ts"), {encoding: "utf8"});
391+
const utilsFiles = await fs.readdir(path.join(PATH_LIB_SRC, UTILS_DIR_NAME));
392+
const indexFile = await fs.readFile(path.join(PATH_LIB_SRC, "index.ts"), {encoding: "utf8"});
390393
for(let file of utilsFiles) {
391394
if(file !== "index.ts" && indexFile.includes(file.split(".ts")[0])) {
392-
let readedFile = await fs.readFile(path.join(pathUtilsDir, file), {encoding: "utf8"});
395+
let readedFile = await fs.readFile(path.join(PATH_LIB_SRC, UTILS_DIR_NAME, file), {encoding: "utf8"});
393396
readedFile = removeImportLine(readedFile);
394397
const jsDoc = buildHooksUtilsMarkdownObject(readedFile);
395398
const title = jsDoc.title.charAt(0).toLowerCase()+jsDoc.title.substring(1)+".md";
396-
await fs.writeFile(path.join(pathMarkdownDir, title), jsDoc2Markdown(jsDoc), {encoding: "utf8"});
399+
await fs.writeFile(path.join(PATH_DEMO_SRC, MARKDOWWN_DIR_NAME, title), jsDoc2Markdown(jsDoc), {encoding: "utf8"});
397400
}
398401
}
399402
}
400403

401404
async function generateHooksMarkDown() {
402-
const hooksDemoDirList = await fs.readdir(path.join(pathHooksDemoDir, "hooks"));
403-
for(let dir of hooksDemoDirList) {
404-
const files = (await fs.readdir(path.join(pathHooksDemoDir, "hooks", dir)));
405-
for(let file of files) {
406-
const hookDemoFileName = file;
407-
if(file.split(".")[0].toLowerCase() === dir.toLowerCase()) {
408-
const extension = hookDemoFileName.endsWith(".tsx") ? ".tsx" : ".md";
409-
let hookDemoFile = await fs.readFile(path.join(pathHooksDemoDir, "hooks", dir, hookDemoFileName), {encoding: "utf8"});
410-
let hookFileName = (hookDemoFileName.charAt(0).toLowerCase() + hookDemoFileName.substring(1)).replace(extension, ".ts");
411-
!existsSync(path.join(pathHooksDir, hookFileName)) && (hookFileName = (hookDemoFileName.charAt(0).toLowerCase() + hookDemoFileName.substring(1)).replace(extension, ".tsx"));
412-
let hookFile = await fs.readFile(path.join(pathHooksDir, hookFileName), {encoding: "utf8"});
413-
hookDemoFile = removeImportLine(hookDemoFile);
414-
hookFile = removeImportLine(hookFile);
415-
const jsDoc = buildHooksUtilsMarkdownObject(hookFile);
416-
jsDoc.usage = buildComponentMarkdown(hookDemoFile, extension);
417-
let title = hookDemoFileName.replace(extension, ".md");
418-
title = title.charAt(0).toLowerCase() + title.substring(1);
419-
await fs.writeFile(path.join(pathMarkdownDir, title), jsDoc2Markdown(jsDoc), {encoding: "utf8"});
405+
const hookTypesDirList = await fs.readdir(path.join(PATH_DEMO_SRC, DEMO_COMPONENT_DIR_NAME, HOOKS_DIR_NAME));
406+
for (const hookTypeDirList of hookTypesDirList) {
407+
const dirsDemoHook = (await fs.readdir(path.join(PATH_DEMO_SRC, DEMO_COMPONENT_DIR_NAME, HOOKS_DIR_NAME, hookTypeDirList)));
408+
for (const demoHookDir of dirsDemoHook) {
409+
const files = (await fs.readdir(path.join(PATH_DEMO_SRC, DEMO_COMPONENT_DIR_NAME, HOOKS_DIR_NAME, hookTypeDirList, demoHookDir)));
410+
for (let file of files) {
411+
const hookDemoFileName = file;
412+
if (file.split(".")[0].toLowerCase() === demoHookDir.toLowerCase()) {
413+
const extension = hookDemoFileName.endsWith(".tsx") ? ".tsx" : ".md";
414+
let hookDemoFile = await fs.readFile(path.join(PATH_DEMO_SRC, DEMO_COMPONENT_DIR_NAME, HOOKS_DIR_NAME, hookTypeDirList, demoHookDir, hookDemoFileName), { encoding: "utf8" });
415+
let hookFileName = (hookDemoFileName.charAt(0).toLowerCase() + hookDemoFileName.substring(1)).replace(extension, ".ts");
416+
!existsSync(path.join(PATH_LIB_SRC, HOOKS_DIR_NAME, hookTypeDirList, hookFileName)) && (hookFileName = (hookDemoFileName.charAt(0).toLowerCase() + hookDemoFileName.substring(1)).replace(extension, ".tsx"));
417+
let hookFile = await fs.readFile(path.join(PATH_LIB_SRC, HOOKS_DIR_NAME, hookTypeDirList, hookFileName), { encoding: "utf8" });
418+
hookDemoFile = removeImportLine(hookDemoFile);
419+
hookFile = removeImportLine(hookFile);
420+
const jsDoc = buildHooksUtilsMarkdownObject(hookFile);
421+
jsDoc.usage = buildComponentMarkdown(hookDemoFile, extension);
422+
let title = hookDemoFileName.replace(extension, ".md");
423+
title = title.charAt(0).toLowerCase() + title.substring(1);
424+
await fs.writeFile(path.join(PATH_DEMO_SRC, MARKDOWWN_DIR_NAME, title), jsDoc2Markdown(jsDoc), { encoding: "utf8" });
425+
}
426+
}
427+
}
428+
}
429+
}
430+
431+
async function generateComponentsMarkDown() {
432+
const componentsDirList = await fs.readdir(path.join(PATH_DEMO_SRC, DEMO_COMPONENT_DIR_NAME, COMPONENTS_DIR_NAME));
433+
for (const componentDir of componentsDirList) {
434+
const files = (await fs.readdir(path.join(PATH_DEMO_SRC, DEMO_COMPONENT_DIR_NAME, COMPONENTS_DIR_NAME, componentDir)));
435+
for (let file of files) {
436+
const componentDemoFileName = file;
437+
if (file.split(".")[0].toLowerCase() === componentDir.toLowerCase()) {
438+
const extension = componentDemoFileName.endsWith(".tsx") ? ".tsx" : ".md";
439+
let componentDemoFile = await fs.readFile(path.join(PATH_DEMO_SRC, DEMO_COMPONENT_DIR_NAME, COMPONENTS_DIR_NAME, componentDir, componentDemoFileName), { encoding: "utf8" });
440+
let componentFileName = componentDemoFileName.replace(extension, ".ts");
441+
!existsSync(path.join(PATH_LIB_SRC, COMPONENTS_DIR_NAME, componentFileName)) && (componentFileName = componentDemoFileName.replace(extension, ".tsx"));
442+
let componentFile = await fs.readFile(path.join(PATH_LIB_SRC, COMPONENTS_DIR_NAME, componentFileName), { encoding: "utf8" });
443+
componentDemoFile = removeImportLine(componentDemoFile);
444+
componentFile = removeImportLine(componentFile);
445+
const jsDoc = buildHooksUtilsMarkdownObject(componentFile);
446+
jsDoc.usage = buildComponentMarkdown(componentDemoFile, extension);
447+
let title = componentDemoFileName.replace(extension, ".md");
448+
await fs.writeFile(path.join(PATH_DEMO_SRC, MARKDOWWN_DIR_NAME, title), jsDoc2Markdown(jsDoc), { encoding: "utf8" });
420449
}
421450
}
422451
}
423452
}
424453

425454
async function generateMarkDown() {
426455
try {
427-
await deleteAllFiles(pathMarkdownDir);
456+
await deleteAllFiles(path.join(PATH_DEMO_SRC, MARKDOWWN_DIR_NAME));
428457
await generateUtilsMarkDown();
429458
await generateHooksMarkDown();
459+
await generateComponentsMarkDown();
430460
process.exit(0);
431461
} catch (error) {
432462
console.error(error);

0 commit comments

Comments
 (0)