Skip to content

Commit 380f38f

Browse files
committed
add generate content script
1 parent da72c1f commit 380f38f

6 files changed

Lines changed: 192 additions & 5 deletions

File tree

build/lib/generateContent.ts

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,81 @@ async function pathExists(path: string): Promise<boolean> {
6363
}
6464
}
6565

66-
export default async function generateContent(): Promise<void> {
66+
async function shouldRegenerateFiles(
67+
contentPath: string,
68+
generatedContentDirPath: string,
69+
frameworkDirPath: string,
70+
): Promise<boolean> {
71+
// Check if any generated files exist
72+
const treeFilePath = path.join(generatedContentDirPath, "tree.js");
73+
const treeDtsFilePath = path.join(generatedContentDirPath, "tree.d.ts");
74+
const frameworkIndexPath = path.join(frameworkDirPath, "index.js");
75+
const frameworkIndexDtsPath = path.join(frameworkDirPath, "index.d.ts");
76+
77+
const generatedFilesExist = await Promise.all([
78+
pathExists(treeFilePath),
79+
pathExists(treeDtsFilePath),
80+
pathExists(frameworkIndexPath),
81+
pathExists(frameworkIndexDtsPath),
82+
]);
83+
84+
// If any generated file is missing, regenerate
85+
if (!generatedFilesExist.every(Boolean)) {
86+
return true;
87+
}
88+
89+
// Get the modification time of the content directory
90+
const contentStats = await fs.stat(contentPath);
91+
const contentModTime = contentStats.mtime;
92+
93+
// Check if any generated file is older than the content directory
94+
const generatedFilePaths = [
95+
treeFilePath,
96+
treeDtsFilePath,
97+
frameworkIndexPath,
98+
frameworkIndexDtsPath,
99+
];
100+
101+
for (const filePath of generatedFilePaths) {
102+
try {
103+
const fileStats = await fs.stat(filePath);
104+
if (fileStats.mtime < contentModTime) {
105+
return true;
106+
}
107+
} catch {
108+
// If we can't stat a file, regenerate to be safe
109+
return true;
110+
}
111+
}
112+
113+
return false;
114+
}
115+
116+
export default async function generateContent(
117+
options: { noCache?: boolean } = {},
118+
): Promise<void> {
67119
const rootDir = await packageDirectory();
68120
if (!rootDir) {
69121
throw new Error("Could not find package directory");
70122
}
71123
const contentPath = path.join(rootDir, "content");
124+
const generatedContentDirPath = path.join(rootDir, "src/generatedContent");
125+
const frameworkDirPath = path.join(generatedContentDirPath, "framework");
126+
127+
// Check if we should skip generation due to cache
128+
if (!options.noCache) {
129+
const shouldRegenerate = await shouldRegenerateFiles(
130+
contentPath,
131+
generatedContentDirPath,
132+
frameworkDirPath,
133+
);
134+
135+
if (!shouldRegenerate) {
136+
console.log("Generated content is up to date, skipping generation.");
137+
return;
138+
}
139+
}
140+
72141
const sectionDirNames = await fs.readdir(contentPath);
73142

74143
const treePayload: TreePayload = {
@@ -185,10 +254,10 @@ export default async function generateContent(): Promise<void> {
185254
}
186255
}
187256

188-
const generatedContentDirPath = path.join(rootDir, "src/generatedContent");
189-
const frameworkDirPath = path.join(generatedContentDirPath, "framework");
190257
const treeFilePath = path.join(generatedContentDirPath, "tree.js");
258+
const treeDtsFilePath = path.join(generatedContentDirPath, "tree.d.ts");
191259
const frameworkIndexPath = path.join(frameworkDirPath, "index.js");
260+
const frameworkIndexDtsPath = path.join(frameworkDirPath, "index.d.ts");
192261
const commentDisclaimer = `// File generated from "node scripts/generateContent.js", DO NOT EDIT/COMMIT`;
193262

194263
if (!(await pathExists(generatedContentDirPath))) {
@@ -204,6 +273,29 @@ export default async function generateContent(): Promise<void> {
204273
`,
205274
);
206275

276+
await writeDtsFile(
277+
treeDtsFilePath,
278+
`
279+
${commentDisclaimer}
280+
export interface Section {
281+
sectionId: string;
282+
sectionDirName: string;
283+
title: string;
284+
}
285+
286+
export interface Snippet {
287+
sectionId: string;
288+
snippetId: string;
289+
snippetDirName: string;
290+
sectionDirName: string;
291+
title: string;
292+
}
293+
294+
export declare const sections: Section[];
295+
export declare const snippets: Snippet[];
296+
`,
297+
);
298+
207299
if (!(await pathExists(frameworkDirPath))) {
208300
await fs.mkdir(frameworkDirPath, { recursive: true });
209301
}
@@ -239,6 +331,18 @@ export default async function generateContent(): Promise<void> {
239331
};
240332
`,
241333
);
334+
335+
await writeDtsFile(
336+
frameworkIndexDtsPath,
337+
`
338+
${commentDisclaimer}
339+
declare const snippetsImporterByFrameworkId: {
340+
[key: string]: () => Promise<any>;
341+
};
342+
343+
export default snippetsImporterByFrameworkId;
344+
`,
345+
);
242346
}
243347

244348
function dirNameToTitle(dirName: string): string {
@@ -254,6 +358,13 @@ async function writeJsFile(filepath: string, jsCode: string): Promise<void> {
254358
await fs.writeFile(filepath, codeFormatted);
255359
}
256360

361+
async function writeDtsFile(filepath: string, dtsCode: string): Promise<void> {
362+
const codeFormatted = await prettier.format(dtsCode, {
363+
parser: "typescript",
364+
});
365+
await fs.writeFile(filepath, codeFormatted);
366+
}
367+
257368
async function generatePlaygroundURL(
258369
frameworkId: string,
259370
files: File[],

lefthook.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@ pre-commit:
44
run: pnpm _format --write {staged_files}
55
glob: "*.{js,ts,svelte,html,md,css}"
66
stage_fixed: true
7+
update-readme-progress:
8+
run: node scripts/generateReadMeProgress.ts && git add README.md
9+
glob: "content/**/*"
10+
stage_fixed: true
11+
fail_text: "Failed to update README progress"

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
"lint": "pnpm format && oxlint && eslint",
1515
"check": "pnpm format && svelte-check --tsconfig ./tsconfig.json && pnpm lint && pnpm test",
1616
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
17-
"build:content": "node scripts/generateContent.ts",
17+
"build:content": "node scripts/generateContent.ts --no-cache",
1818
"build:progress": "node scripts/generateReadMeProgress.ts",
19+
"test:content-hook": "node scripts/checkContentChanges.ts",
1920
"prepare": "lefthook install",
2021
"test:e2e": "playwright test",
2122
"test:e2e:ui": "playwright test --ui",

scripts/checkContentChanges.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { execSync } from "child_process";
2+
import fs from "fs/promises";
3+
import path from "node:path";
4+
5+
async function main(): Promise<void> {
6+
try {
7+
// Check if there are any changes in the content directory
8+
const contentChanges = execSync("git diff --name-only HEAD~1 HEAD", {
9+
encoding: "utf8",
10+
})
11+
.split("\n")
12+
.filter((file) => file.startsWith("content/"));
13+
14+
if (contentChanges.length === 0) {
15+
console.log(
16+
"No changes detected in content directory, skipping README update.",
17+
);
18+
return;
19+
}
20+
21+
console.log(
22+
`Detected changes in content directory: ${contentChanges.join(", ")}`,
23+
);
24+
console.log("Running generateReadMeProgress script...");
25+
26+
// Run the generateReadMeProgress script
27+
execSync("node scripts/generateReadMeProgress.ts", { stdio: "inherit" });
28+
29+
// Check if README.md was modified
30+
const readmeChanges = execSync("git diff --name-only", { encoding: "utf8" })
31+
.split("\n")
32+
.filter((file) => file === "README.md");
33+
34+
if (readmeChanges.length > 0) {
35+
console.log("README.md was updated, committing changes...");
36+
37+
// Add and commit the README.md changes
38+
execSync("git add README.md", { stdio: "inherit" });
39+
execSync(
40+
'git commit -m "docs: update README progress based on content changes"',
41+
{ stdio: "inherit" },
42+
);
43+
44+
console.log("README progress update committed successfully!");
45+
} else {
46+
console.log("No changes to README.md were made.");
47+
}
48+
} catch (error) {
49+
console.error("Error in checkContentChanges script:", error);
50+
process.exit(1);
51+
}
52+
}
53+
54+
main().catch(console.error);

scripts/generateContent.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env node
2+
3+
import generateContent from "../build/lib/generateContent.ts";
4+
5+
const args = process.argv.slice(2);
6+
const noCache = args.includes("--no-cache") || args.includes("--force");
7+
8+
console.log(`Generating content${noCache ? " (no cache)" : ""}...`);
9+
10+
try {
11+
await generateContent({ noCache });
12+
console.log("Content generation completed successfully!");
13+
} catch (error) {
14+
console.error("Error generating content:", error);
15+
process.exit(1);
16+
}

src/Index.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@
288288
</h2>
289289
{#if frameworkIdsSelectedInitialized}
290290
<div
291-
class="grid grid-cols-1 2xl:grid-cols-2 gap-y-4 2xl:gap-y-8 gap-x-10 mt-2"
291+
class="grid grid-cols-1 xl:grid-cols-2 gap-y-4 xl:gap-y-8 gap-x-10 mt-2"
292292
>
293293
{#each frameworkIdsSelectedArr as frameworkId (frameworkId)}
294294
{@const framework = matchFrameworkId(frameworkId)}

0 commit comments

Comments
 (0)