Skip to content

Commit 7054436

Browse files
authored
update file structure
1 parent 1091093 commit 7054436

File tree

8 files changed

+342
-40
lines changed

8 files changed

+342
-40
lines changed
File renamed without changes.
File renamed without changes.

scripts/jsonGenerator.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import fs from "node:fs";
2+
import path from "node:path";
3+
import matter from "gray-matter";
4+
5+
const CONTENT_DEPTH = 2;
6+
const JSON_FOLDER = "./.json";
7+
const BLOG_FOLDER = "src/content/blog";
8+
9+
// get data from markdown
10+
const getData = (folder, groupDepth) => {
11+
const getPath = fs.readdirSync(folder);
12+
const removeIndex = getPath.filter((item) => !item.startsWith("-"));
13+
14+
const getPaths = removeIndex.flatMap((filename) => {
15+
const filepath = path.join(folder, filename);
16+
const stats = fs.statSync(filepath);
17+
const isFolder = stats.isDirectory();
18+
19+
if (isFolder) {
20+
return getData(filepath, groupDepth);
21+
} else if (filename.endsWith(".md") || filename.endsWith(".mdx")) {
22+
const file = fs.readFileSync(filepath, "utf-8");
23+
const { data, content } = matter(file);
24+
const pathParts = filepath.split(path.sep);
25+
const slug =
26+
data.slug ||
27+
pathParts
28+
.slice(CONTENT_DEPTH)
29+
.join("/")
30+
.replace(/\.[^/.]+$/, "");
31+
const group = pathParts[groupDepth];
32+
33+
return {
34+
group: group,
35+
slug: slug,
36+
frontmatter: data,
37+
content: content,
38+
};
39+
} else {
40+
return [];
41+
}
42+
});
43+
44+
return getPaths.filter((page) => !page.frontmatter?.draft && page);
45+
};
46+
47+
try {
48+
// create folder if it doesn't exist
49+
if (!fs.existsSync(JSON_FOLDER)) {
50+
fs.mkdirSync(JSON_FOLDER);
51+
}
52+
53+
// create json files
54+
fs.writeFileSync(
55+
`${JSON_FOLDER}/posts.json`,
56+
JSON.stringify(getData(BLOG_FOLDER, 2)),
57+
);
58+
59+
// merger json files for search
60+
const postsPath = new URL(`../${JSON_FOLDER}/posts.json`, import.meta.url);
61+
const posts = JSON.parse(fs.readFileSync(postsPath, "utf8"));
62+
const search = [...posts];
63+
fs.writeFileSync(`${JSON_FOLDER}/search.json`, JSON.stringify(search));
64+
} catch (err) {
65+
console.error(err);
66+
}

scripts/removeDarkmode.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import fs from "node:fs";
2+
import path from "node:path";
3+
4+
(function () {
5+
const rootDirs = ["src/pages", "src/hooks", "src/layouts", "src/styles"];
6+
7+
const deleteAssetList = [
8+
"public/images/logo-darkmode.png",
9+
"src/layouts/components/ThemeSwitcher.astro",
10+
];
11+
12+
const configFiles = [
13+
{ filePath: "src/config/theme.json", patterns: ["colors.darkmode"] },
14+
];
15+
16+
const filePaths = [
17+
{
18+
filePath: "src/layouts/partials/Header.astro",
19+
patterns: [
20+
"<ThemeSwitchers*(?:\\s+[^>]+)?\\s*(?:\\/\\>|>([\\s\\S]*?)<\\/ThemeSwitchers*>)",
21+
],
22+
},
23+
];
24+
25+
filePaths.forEach(({ filePath, patterns }) => {
26+
removeDarkModeFromFiles(filePath, patterns);
27+
});
28+
29+
deleteAssetList.forEach(deleteAsset);
30+
function deleteAsset(asset) {
31+
try {
32+
fs.unlinkSync(asset);
33+
console.log(`${path.basename(asset)} deleted successfully!`);
34+
} catch (error) {
35+
console.error(`${asset} not found`);
36+
}
37+
}
38+
39+
rootDirs.forEach(removeDarkModeFromPages);
40+
configFiles.forEach(removeDarkMode);
41+
42+
function removeDarkModeFromFiles(filePath, regexPatterns) {
43+
const fileContent = fs.readFileSync(filePath, "utf8");
44+
let updatedContent = fileContent;
45+
regexPatterns.forEach((pattern) => {
46+
const regex = new RegExp(pattern, "g");
47+
updatedContent = updatedContent.replace(regex, "");
48+
});
49+
fs.writeFileSync(filePath, updatedContent, "utf8");
50+
}
51+
52+
function removeDarkModeFromPages(directoryPath) {
53+
const files = fs.readdirSync(directoryPath);
54+
55+
files.forEach((file) => {
56+
const filePath = path.join(directoryPath, file);
57+
const stats = fs.statSync(filePath);
58+
if (stats.isDirectory()) {
59+
removeDarkModeFromPages(filePath);
60+
} else if (stats.isFile()) {
61+
removeDarkModeFromFiles(filePath, [
62+
'(?:(?!["])\\S)*dark:(?:(?![,;"])\\S)*',
63+
]);
64+
}
65+
});
66+
}
67+
68+
function removeDarkMode(configFile) {
69+
const { filePath, patterns } = configFile;
70+
const contentFile = JSON.parse(fs.readFileSync(filePath, "utf8"));
71+
patterns.forEach((pattern) => deleteNestedProperty(contentFile, pattern));
72+
fs.writeFileSync(filePath, JSON.stringify(contentFile));
73+
}
74+
75+
function deleteNestedProperty(obj, propertyPath) {
76+
const properties = propertyPath.split(".");
77+
let currentObj = obj;
78+
for (let i = 0; i < properties.length - 1; i++) {
79+
const property = properties[i];
80+
if (currentObj.hasOwnProperty(property)) {
81+
currentObj = currentObj[property];
82+
} else {
83+
return; // Property not found, no need to continue
84+
}
85+
}
86+
delete currentObj[properties[properties.length - 1]];
87+
}
88+
})();

scripts/themeGenerator.js

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import fs from "fs";
2+
import path from "path";
3+
import { fileURLToPath } from "url";
4+
5+
const __filename = fileURLToPath(import.meta.url);
6+
const __dirname = path.dirname(__filename);
7+
8+
/**
9+
* Determine the paths based on setup mode (theme vs project)
10+
* @returns {Object} Configuration object with paths and mode info
11+
*/
12+
function determinePaths() {
13+
const themePath = path.join(__dirname, "../src/config/theme.json");
14+
const outputPath = path.join(__dirname, "../src/styles/generated-theme.css");
15+
16+
if (!fs.existsSync(themePath)) {
17+
throw new Error(`Could not find theme.json at: ${themePath}`);
18+
}
19+
20+
return { themePath, outputPath };
21+
}
22+
23+
const { themePath, outputPath } = determinePaths();
24+
25+
// Helper to convert color name from snake_case to kebab-case
26+
const toKebab = (str) => str.replace(/_/g, "-");
27+
28+
// Helper to extract a clean font name
29+
const findFont = (fontStr) =>
30+
fontStr.replace(/\+/g, " ").replace(/:[^:]+/g, "");
31+
32+
/**
33+
* Add color entries to CSS array
34+
* @param {Array} cssLines - Array of CSS lines to append to
35+
* @param {Object} colors - Color object to process
36+
* @param {string} prefix - Optional prefix for color variable names
37+
*/
38+
function addColorsToCss(cssLines, colors, prefix = "") {
39+
Object.entries(colors).forEach(([key, value]) => {
40+
const colorName = prefix
41+
? `--color-${prefix}-${toKebab(key)}`
42+
: `--color-${toKebab(key)}`;
43+
cssLines.push(` ${colorName}: ${value};`);
44+
});
45+
}
46+
47+
/**
48+
* Generate theme CSS from theme.json configuration
49+
* @throws {Error} If theme.json is missing or invalid
50+
*/
51+
function generateThemeCSS() {
52+
// Validate that theme.json exists
53+
if (!fs.existsSync(themePath)) {
54+
throw new Error(`Theme configuration not found: ${themePath}`);
55+
}
56+
57+
try {
58+
// Read and parse theme configuration
59+
const themeConfig = JSON.parse(fs.readFileSync(themePath, "utf8"));
60+
61+
// Validate required theme structure
62+
if (!themeConfig.colors || !themeConfig.fonts) {
63+
throw new Error(
64+
"Invalid theme.json: missing 'colors' or 'fonts' section",
65+
);
66+
}
67+
68+
// Build CSS using array for better performance
69+
const cssLines = [
70+
"/**",
71+
' * Auto-generated from "src/config/theme.json"',
72+
" * DO NOT EDIT THIS FILE MANUALLY",
73+
" * Run: node scripts/themeGenerator.js",
74+
" */",
75+
"",
76+
"@theme {",
77+
" /* === Colors === */",
78+
];
79+
80+
// Add default theme colors
81+
if (themeConfig.colors.default?.theme_color) {
82+
addColorsToCss(cssLines, themeConfig.colors.default.theme_color);
83+
}
84+
85+
// Add default text colors
86+
if (themeConfig.colors.default?.text_color) {
87+
addColorsToCss(cssLines, themeConfig.colors.default.text_color);
88+
}
89+
90+
// Add darkmode colors (if available)
91+
if (themeConfig.colors.darkmode) {
92+
cssLines.push("", " /* === Darkmode Colors === */");
93+
94+
if (themeConfig.colors.darkmode.theme_color) {
95+
addColorsToCss(
96+
cssLines,
97+
themeConfig.colors.darkmode.theme_color,
98+
"darkmode",
99+
);
100+
}
101+
102+
if (themeConfig.colors.darkmode.text_color) {
103+
addColorsToCss(
104+
cssLines,
105+
themeConfig.colors.darkmode.text_color,
106+
"darkmode",
107+
);
108+
}
109+
}
110+
111+
// Add font families
112+
cssLines.push("", " /* === Font Families === */");
113+
const fontFamily = themeConfig.fonts.font_family || {};
114+
Object.entries(fontFamily)
115+
.filter(([key]) => !key.includes("type"))
116+
.forEach(([key, font]) => {
117+
const fontFallback = fontFamily[`${key}_type`] || "sans-serif";
118+
const fontValue = `${findFont(font)}, ${fontFallback}`;
119+
cssLines.push(` --font-${toKebab(key)}: ${fontValue};`);
120+
});
121+
122+
// Add font sizes
123+
cssLines.push("", " /* === Font Sizes === */");
124+
const baseSize = Number(themeConfig.fonts.font_size?.base || 16);
125+
const scale = Number(themeConfig.fonts.font_size?.scale || 1.25);
126+
127+
cssLines.push(` --text-base: ${baseSize}px;`);
128+
cssLines.push(` --text-base-sm: ${baseSize * 0.8}px;`);
129+
130+
let currentSize = scale;
131+
for (let i = 6; i >= 1; i--) {
132+
cssLines.push(` --text-h${i}: ${currentSize}rem;`);
133+
cssLines.push(` --text-h${i}-sm: ${currentSize * 0.9}rem;`);
134+
currentSize *= scale;
135+
}
136+
137+
cssLines.push("}");
138+
139+
// Ensure output directory exists
140+
const outputDir = path.dirname(outputPath);
141+
if (!fs.existsSync(outputDir)) {
142+
fs.mkdirSync(outputDir, { recursive: true });
143+
}
144+
145+
// Write the file
146+
fs.writeFileSync(outputPath, cssLines.join("\n") + "\n");
147+
console.log("✅ Theme CSS generated successfully at:", outputPath);
148+
} catch (error) {
149+
throw new Error(`Failed to generate theme CSS: ${error.message}`);
150+
}
151+
}
152+
153+
// Generate CSS on startup
154+
try {
155+
generateThemeCSS();
156+
} catch (error) {
157+
console.error("❌ Error:", error.message);
158+
process.exit(1);
159+
}
160+
161+
// Check for --watch flag
162+
if (process.argv.includes("--watch")) {
163+
let debounceTimer;
164+
165+
const watcher = fs.watch(themePath, (eventType) => {
166+
if (eventType === "change") {
167+
// Debounce to avoid multiple triggers
168+
clearTimeout(debounceTimer);
169+
debounceTimer = setTimeout(() => {
170+
try {
171+
generateThemeCSS();
172+
} catch (error) {
173+
console.error("❌ Error regenerating theme CSS:", error.message);
174+
}
175+
}, 300);
176+
}
177+
});
178+
179+
// Handle graceful shutdown
180+
process.on("SIGINT", () => {
181+
clearTimeout(debounceTimer);
182+
watcher.close();
183+
console.log("\n👋 Watcher stopped");
184+
process.exit(0);
185+
});
186+
187+
console.log("👁️ Watching for changes to:", themePath);
188+
}

src/lib/utils/readingTime.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)