Skip to content

Commit 7b95db1

Browse files
author
Song Mingxu
committed
build: generate topic sites during root build
1 parent e1d9ff1 commit 7b95db1

5 files changed

Lines changed: 148 additions & 12 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"type": "module",
55
"scripts": {
66
"dev": "astro dev",
7-
"build": "astro build && node scripts/copy-topic-sites.mjs",
7+
"build:topics": "node scripts/build-topic-sites.mjs",
8+
"build": "npm run build:topics && astro build && node scripts/copy-topic-sites.mjs",
89
"preview": "astro preview",
910
"check": "astro check"
1011
},

scripts/build-topic-sites.mjs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { existsSync } from "node:fs";
2+
import path from "node:path";
3+
import { spawnSync } from "node:child_process";
4+
5+
import { findBuildableTopics } from "./topic-sites.mjs";
6+
7+
const rootDir = process.cwd();
8+
const topics = findBuildableTopics(rootDir);
9+
10+
for (const topic of topics) {
11+
const hasNodeModules = existsSync(path.join(topic.dir, "node_modules"));
12+
const hasLockfile = existsSync(path.join(topic.dir, "package-lock.json"));
13+
14+
if (!hasNodeModules) {
15+
const installArgs = hasLockfile ? ["ci"] : ["install"];
16+
const install = spawnSync("npm", installArgs, {
17+
cwd: topic.dir,
18+
stdio: "inherit",
19+
shell: process.platform === "win32",
20+
});
21+
22+
if (install.status !== 0) {
23+
process.exit(install.status ?? 1);
24+
}
25+
}
26+
27+
const build = spawnSync("npm", ["run", "build"], {
28+
cwd: topic.dir,
29+
stdio: "inherit",
30+
shell: process.platform === "win32",
31+
});
32+
33+
if (build.status !== 0) {
34+
process.exit(build.status ?? 1);
35+
}
36+
}

scripts/copy-topic-sites.mjs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
1-
import { cpSync, existsSync, mkdirSync, readdirSync } from "node:fs";
1+
import { cpSync, existsSync, mkdirSync } from "node:fs";
22
import path from "node:path";
3+
import { findBuildableTopics, resolveTopicPublishSourceDir } from "./topic-sites.mjs";
34

45
const rootDir = process.cwd();
5-
const topicsDir = path.join(rootDir, "topics");
66
const distDir = path.join(rootDir, "dist");
77

8-
if (!existsSync(topicsDir) || !existsSync(distDir)) {
8+
if (!existsSync(distDir)) {
99
process.exit(0);
1010
}
1111

12-
for (const entry of readdirSync(topicsDir, { withFileTypes: true })) {
13-
if (!entry.isDirectory()) continue;
14-
15-
const topicDir = path.join(topicsDir, entry.name);
16-
const preferredSourceDir = path.join(topicDir, "site");
17-
const fallbackSourceDir = path.join(topicDir, "dist");
18-
const sourceDir = existsSync(preferredSourceDir) ? preferredSourceDir : fallbackSourceDir;
12+
for (const topic of findBuildableTopics(rootDir)) {
13+
const sourceDir = resolveTopicPublishSourceDir(topic.dir);
1914

2015
if (!existsSync(sourceDir)) continue;
2116

22-
const targetDir = path.join(distDir, "topics", entry.name, "site");
17+
const targetDir = path.join(distDir, "topics", topic.name, "site");
2318
mkdirSync(path.dirname(targetDir), { recursive: true });
2419
cpSync(sourceDir, targetDir, { recursive: true });
2520
}

scripts/topic-sites.mjs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { existsSync, readFileSync, readdirSync } from "node:fs";
2+
import path from "node:path";
3+
4+
export function findBuildableTopics(rootDir) {
5+
const topicsDir = path.join(rootDir, "topics");
6+
7+
if (!existsSync(topicsDir)) {
8+
return [];
9+
}
10+
11+
return readdirSync(topicsDir, { withFileTypes: true })
12+
.filter((entry) => entry.isDirectory())
13+
.map((entry) => {
14+
const topicDir = path.join(topicsDir, entry.name);
15+
const packageJsonPath = path.join(topicDir, "package.json");
16+
17+
if (!existsSync(packageJsonPath)) {
18+
return null;
19+
}
20+
21+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
22+
23+
return {
24+
name: entry.name,
25+
dir: topicDir,
26+
packageJson,
27+
};
28+
})
29+
.filter(Boolean)
30+
.filter((topic) => topic.packageJson?.scripts?.build);
31+
}
32+
33+
export function resolveTopicPublishSourceDir(topicDir) {
34+
const distDir = path.join(topicDir, "dist");
35+
const siteDir = path.join(topicDir, "site");
36+
37+
if (existsSync(distDir)) {
38+
return distDir;
39+
}
40+
41+
if (existsSync(siteDir)) {
42+
return siteDir;
43+
}
44+
45+
return null;
46+
}

scripts/topic-sites.test.mjs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import test from "node:test";
2+
import assert from "node:assert/strict";
3+
import os from "node:os";
4+
import path from "node:path";
5+
import { mkdirSync, writeFileSync } from "node:fs";
6+
7+
import {
8+
findBuildableTopics,
9+
resolveTopicPublishSourceDir,
10+
} from "./topic-sites.mjs";
11+
12+
test("findBuildableTopics discovers topic apps with package.json", () => {
13+
const rootDir = path.join(os.tmpdir(), `topic-sites-${Date.now()}-discover`);
14+
const topicsDir = path.join(rootDir, "topics");
15+
16+
mkdirSync(path.join(topicsDir, "alpha"), { recursive: true });
17+
mkdirSync(path.join(topicsDir, "beta"), { recursive: true });
18+
mkdirSync(path.join(topicsDir, "docs-only"), { recursive: true });
19+
20+
writeFileSync(
21+
path.join(topicsDir, "alpha", "package.json"),
22+
JSON.stringify({ name: "alpha", scripts: { build: "vite build" } }),
23+
);
24+
writeFileSync(
25+
path.join(topicsDir, "beta", "package.json"),
26+
JSON.stringify({ name: "beta", scripts: { build: "vite build" } }),
27+
);
28+
29+
const topics = findBuildableTopics(rootDir);
30+
31+
assert.deepEqual(
32+
topics.map((topic) => topic.name),
33+
["alpha", "beta"],
34+
);
35+
});
36+
37+
test("resolveTopicPublishSourceDir prefers dist over stale site output", () => {
38+
const topicDir = path.join(os.tmpdir(), `topic-sites-${Date.now()}-source`);
39+
40+
mkdirSync(path.join(topicDir, "site"), { recursive: true });
41+
mkdirSync(path.join(topicDir, "dist"), { recursive: true });
42+
43+
assert.equal(
44+
resolveTopicPublishSourceDir(topicDir),
45+
path.join(topicDir, "dist"),
46+
);
47+
});
48+
49+
test("resolveTopicPublishSourceDir falls back to site when dist is missing", () => {
50+
const topicDir = path.join(os.tmpdir(), `topic-sites-${Date.now()}-fallback`);
51+
52+
mkdirSync(path.join(topicDir, "site"), { recursive: true });
53+
54+
assert.equal(
55+
resolveTopicPublishSourceDir(topicDir),
56+
path.join(topicDir, "site"),
57+
);
58+
});

0 commit comments

Comments
 (0)