-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmod.ts
121 lines (100 loc) · 4.23 KB
/
mod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { type SiteConfig, loadConfig } from "./src/config.ts";
import type { ThemeConfig } from "./src/theme/config.ts";
import { ensureDirSync } from "./src/fileUtils.ts";
import { parseFrontmatter } from "./src/frontmatter.ts";
import type { wrapWithTemplate as _wrapWithTemplate } from "./src/template.ts";
import { startDevServer } from "./src/server.ts";
import { marked } from "https://esm.sh/[email protected]";
import { Liquid } from "https://esm.sh/[email protected]";
import { join } from "jsr:@std/path";
import { parse as YAML } from "jsr:@std/yaml";
export class Steno {
private config: SiteConfig;
private theme?: Theme;
constructor(configPath: string = 'content/.steno/config.yml') {
this.config = loadConfig(configPath);
if (this.config.custom?.theme) {
this.theme = new Theme(this.config.custom.theme, `@stenothemes/${this.config.custom.theme}/theme.css`);
}
this.init();
}
public build(): void {
const contentDir = this.config.contentDir || 'content';
const outputDir = this.config.output || 'dist';
ensureDirSync(outputDir);
for (const file of Deno.readDirSync(contentDir)) {
const filePath = join(contentDir, file.name);
if (Deno.statSync(filePath).isFile) {
const fileContents = Deno.readTextFileSync(filePath);
// Parse frontmatter and content body
const { frontmatter, body } = parseFrontmatter(fileContents);
// Convert Markdown to HTML
const htmlContent = marked(body);
// Determine output file path
let outputFilePath = join(outputDir, file.name.replace('.md', '.html'));
if (this.config.custom?.shortUrls) {
if (file.name !== 'index.md') {
outputFilePath = join(outputDir, file.name.replace('.md', ''));
ensureDirSync(outputFilePath);
outputFilePath = join(outputFilePath, 'index.html');
} else {
outputFilePath = join(outputDir, 'index.html');
}
}
// Render using the theme's layout
const renderedContent = this.theme?.renderLayout("layout", htmlContent, {
title: frontmatter.title || this.config.title,
...frontmatter,
}) || htmlContent;
// Write the rendered content to the output file
Deno.writeTextFileSync(outputFilePath, renderedContent);
}
}
console.log("Build complete.");
}
public async dev(): Promise<void> {
await startDevServer(this.config.output || 'dist');
}
private init() {
if (Deno.args.includes("dev")) {
this.dev();
} else {
this.build();
}
}
}
export class Theme {
private config: ThemeConfig;
private layoutsDir: string;
private componentsDir: string;
constructor(themeName: string, themePath: string) {
this.config = this.loadThemeConfig(themeName, themePath);
this.layoutsDir = join(themePath, this.config.layoutsDir || "layouts");
this.componentsDir = join(themePath, this.config.componentsDir || "components");
}
private loadThemeConfig(_themeName: string, themePath: string): ThemeConfig {
const configFilePath = themePath.endsWith("theme.yml")
? themePath // Use full path if provided
: join(themePath, "theme.yml"); // Otherwise, construct path
console.log("Attempting to read theme config file at:", configFilePath);
const configContent = Deno.readTextFileSync(configFilePath);
return YAML(configContent) as ThemeConfig;
}
public renderLayout(layoutName: string, content: string, variables: Record<string, any>): string {
const layoutPath = join(this.layoutsDir, `${layoutName}.liquid`);
const layoutTemplate = Deno.readTextFileSync(layoutPath);
return this.renderTemplate(layoutTemplate, { content, ...variables });
}
public renderComponent(componentName: string, variables: Record<string, any>): string {
const componentPath = join(this.componentsDir, `${componentName}.liquid`);
const componentTemplate = Deno.readTextFileSync(componentPath);
return this.renderTemplate(componentTemplate, variables);
}
private renderTemplate(template: string, variables: Record<string, any>): string {
const liquid = new Liquid({
root: this.layoutsDir,
extname: ".liquid",
});
return liquid.parseAndRenderSync(template, variables);
}
}