stropress 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +307 -0
  3. package/dist/theme-default/package.json +20 -0
  4. package/dist/theme-default/postcss.config.mjs +5 -0
  5. package/dist/theme-default/src/components/BaseHead.astro +53 -0
  6. package/dist/theme-default/src/components/DocToc.astro +111 -0
  7. package/dist/theme-default/src/components/GithubIcon.astro +35 -0
  8. package/dist/theme-default/src/components/HomePage.astro +198 -0
  9. package/dist/theme-default/src/components/Icon.astro +78 -0
  10. package/dist/theme-default/src/components/LocaleSelect.astro +187 -0
  11. package/dist/theme-default/src/components/NavBar.astro +231 -0
  12. package/dist/theme-default/src/components/SearchInput.astro +84 -0
  13. package/dist/theme-default/src/components/SearchModal.astro +209 -0
  14. package/dist/theme-default/src/components/Sidebar.astro +101 -0
  15. package/dist/theme-default/src/content/docs/guide/configuration.mdx +195 -0
  16. package/dist/theme-default/src/content/docs/guide/getting-started.md +98 -0
  17. package/dist/theme-default/src/content/docs/guide/search.mdx +41 -0
  18. package/dist/theme-default/src/content/docs/index.css +0 -0
  19. package/dist/theme-default/src/content/docs/index.md +6 -0
  20. package/dist/theme-default/src/content/docs/zh/guide/configuration.mdx +149 -0
  21. package/dist/theme-default/src/content/docs/zh/guide/getting-started.md +31 -0
  22. package/dist/theme-default/src/content/docs/zh/guide/search.mdx +23 -0
  23. package/dist/theme-default/src/content/docs/zh/index.astro +75 -0
  24. package/dist/theme-default/src/content/docs/zh/index.md +6 -0
  25. package/dist/theme-default/src/content.config.ts +14 -0
  26. package/dist/theme-default/src/env.d.ts +15 -0
  27. package/dist/theme-default/src/layouts/DocsLayout.astro +278 -0
  28. package/dist/theme-default/src/lib/config.ts +195 -0
  29. package/dist/theme-default/src/lib/custom-home.ts +40 -0
  30. package/dist/theme-default/src/lib/custom-style.ts +11 -0
  31. package/dist/theme-default/src/lib/og.ts +275 -0
  32. package/dist/theme-default/src/pages/[...slug]/index.astro +83 -0
  33. package/dist/theme-default/src/pages/index.astro +47 -0
  34. package/dist/theme-default/src/pages/og/[...slug].png.ts +70 -0
  35. package/dist/theme-default/src/scripts/code-copy.ts +54 -0
  36. package/dist/theme-default/src/scripts/doc-toc.ts +109 -0
  37. package/dist/theme-default/src/scripts/search.ts +329 -0
  38. package/dist/theme-default/src/styles/global.css +70 -0
  39. package/dist/theme-default/src/styles/markdown.css +141 -0
  40. package/dist/theme-default/tsconfig.json +10 -0
  41. package/package.json +32 -0
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,307 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { build, dev } from "astro";
5
+ import { program } from "commander";
6
+ import fs from "fs-extra";
7
+ import { watch } from "node:fs";
8
+ import path from "node:path";
9
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
+ var currentFilePath = fileURLToPath(import.meta.url);
11
+ var currentDir = path.dirname(currentFilePath);
12
+ var run = async (mode, options) => {
13
+ const cwd = process.cwd();
14
+ const docsDir = path.resolve(cwd, options.dir || "docs");
15
+ const configPath = path.join(docsDir, "config.json");
16
+ const themeDir = await resolveThemeDir();
17
+ const themeContentDir = path.join(themeDir, "src/content/docs");
18
+ const loadAstroConfig = async (config2) => {
19
+ const astroConfigPath = await writeAstroConfig({
20
+ cwd,
21
+ themeDir,
22
+ config: config2
23
+ });
24
+ const astroConfigModule = await import(`${pathToFileURL(astroConfigPath).href}?t=${Date.now()}`);
25
+ return astroConfigModule.default;
26
+ };
27
+ if (!await fs.pathExists(themeDir)) {
28
+ throw new Error(`Missing theme directory: ${themeDir}`);
29
+ }
30
+ if (!await fs.pathExists(docsDir)) {
31
+ throw new Error(`Missing docs directory: ${docsDir}`);
32
+ }
33
+ const docSources = await collectDocSources(docsDir);
34
+ if (docSources.length === 0) {
35
+ throw new Error("No supported docs files found under docs/. Add .md/.mdx content or index.astro homepage files.");
36
+ }
37
+ await syncDocs(docsDir, themeContentDir);
38
+ if (mode === "dev") {
39
+ let devServer;
40
+ let restarting = false;
41
+ let pendingRestart = false;
42
+ const startDevServer = async () => {
43
+ const config2 = await readConfig(configPath);
44
+ const astroConfig2 = await loadAstroConfig(config2);
45
+ devServer = await dev({
46
+ ...astroConfig2,
47
+ root: themeDir,
48
+ site: astroConfig2.site,
49
+ devOutput: "static"
50
+ });
51
+ };
52
+ const restartDevServer = async () => {
53
+ if (restarting) {
54
+ pendingRestart = true;
55
+ return;
56
+ }
57
+ restarting = true;
58
+ try {
59
+ console.log("[stropress] Detected config.json changes. Restarting dev server...");
60
+ if (devServer) {
61
+ await devServer.stop();
62
+ }
63
+ await startDevServer();
64
+ console.log("[stropress] Dev server restarted.");
65
+ } catch (error) {
66
+ const message = error instanceof Error ? error.message : String(error);
67
+ console.error(`[stropress] Failed to restart dev server: ${message}`);
68
+ } finally {
69
+ restarting = false;
70
+ if (pendingRestart) {
71
+ pendingRestart = false;
72
+ restartDevServer();
73
+ }
74
+ }
75
+ };
76
+ await startDevServer();
77
+ const stopWatching = watchDocsForChanges({
78
+ sourceDir: docsDir,
79
+ targetDir: themeContentDir,
80
+ onConfigChange: restartDevServer
81
+ });
82
+ const shutdown = () => {
83
+ stopWatching();
84
+ if (devServer) {
85
+ devServer.stop();
86
+ }
87
+ };
88
+ process.once("SIGINT", shutdown);
89
+ process.once("SIGTERM", shutdown);
90
+ return;
91
+ }
92
+ const config = await readConfig(configPath);
93
+ const astroConfig = await loadAstroConfig(config);
94
+ await build({
95
+ ...astroConfig,
96
+ root: themeDir,
97
+ site: astroConfig.site,
98
+ devOutput: "static"
99
+ });
100
+ };
101
+ var resolveThemeDir = async () => {
102
+ const candidates = [
103
+ path.resolve(currentDir, "theme-default"),
104
+ path.resolve(currentDir, "../theme-default"),
105
+ path.resolve(currentDir, "../../themes/default")
106
+ ];
107
+ for (const candidate of candidates) {
108
+ if (await fs.pathExists(candidate)) {
109
+ return candidate;
110
+ }
111
+ }
112
+ throw new Error("Missing bundled default theme");
113
+ };
114
+ var collectDocSources = async (currentDir2) => {
115
+ const entries = await fs.readdir(currentDir2, { withFileTypes: true });
116
+ const files = [];
117
+ for (const entry of entries) {
118
+ if (entry.name === "config.json") {
119
+ continue;
120
+ }
121
+ const fullPath = path.join(currentDir2, entry.name);
122
+ if (entry.isDirectory()) {
123
+ files.push(...await collectDocSources(fullPath));
124
+ continue;
125
+ }
126
+ if (isSupportedDocFile(entry.name, fullPath)) {
127
+ files.push(fullPath);
128
+ }
129
+ }
130
+ return files;
131
+ };
132
+ var isSupportedDocFile = (fileName, filePath) => {
133
+ if (/\.(md|mdx)$/i.test(fileName)) {
134
+ return true;
135
+ }
136
+ if (fileName === "index.css" && isRootCustomStyleFile(filePath)) {
137
+ return true;
138
+ }
139
+ return fileName === "index.astro" && isHomepageAstroFile(filePath);
140
+ };
141
+ var isHomepageAstroFile = (filePath) => {
142
+ const normalizedPath = filePath.replaceAll("\\", "/");
143
+ return /(^|\/)index\.astro$/i.test(normalizedPath);
144
+ };
145
+ var isRootCustomStyleFile = (filePath) => {
146
+ const normalizedPath = filePath.replaceAll("\\", "/");
147
+ return /(^|\/)docs\/index\.css$/i.test(normalizedPath);
148
+ };
149
+ var readConfig = async (configPath) => {
150
+ if (!await fs.pathExists(configPath)) {
151
+ return {};
152
+ }
153
+ return fs.readJson(configPath);
154
+ };
155
+ var syncDocs = async (sourceDir, targetDir) => {
156
+ await fs.emptyDir(targetDir);
157
+ await copyDocsRecursive(sourceDir, targetDir);
158
+ };
159
+ var watchDocsForChanges = (input) => {
160
+ let timer;
161
+ let configTimer;
162
+ let syncing = false;
163
+ let pending = false;
164
+ const runSync = async () => {
165
+ if (syncing) {
166
+ pending = true;
167
+ return;
168
+ }
169
+ syncing = true;
170
+ try {
171
+ await syncDocs(input.sourceDir, input.targetDir);
172
+ console.log("[stropress] Synced docs changes.");
173
+ } catch (error) {
174
+ const message = error instanceof Error ? error.message : String(error);
175
+ console.error(`[stropress] Failed to sync docs changes: ${message}`);
176
+ } finally {
177
+ syncing = false;
178
+ if (pending) {
179
+ pending = false;
180
+ runSync();
181
+ }
182
+ }
183
+ };
184
+ const queueSync = () => {
185
+ if (timer) {
186
+ clearTimeout(timer);
187
+ }
188
+ timer = setTimeout(() => {
189
+ runSync();
190
+ }, 120);
191
+ };
192
+ const queueConfigRestart = () => {
193
+ if (configTimer) {
194
+ clearTimeout(configTimer);
195
+ }
196
+ configTimer = setTimeout(() => {
197
+ input.onConfigChange?.();
198
+ }, 160);
199
+ };
200
+ const watcher = watch(input.sourceDir, { recursive: true }, (_eventType, filename) => {
201
+ const changedPath = typeof filename === "string" ? filename : "";
202
+ const normalizedPath = changedPath.replaceAll("\\", "/");
203
+ if (!changedPath) {
204
+ queueSync();
205
+ return;
206
+ }
207
+ if (normalizedPath === "config.json") {
208
+ queueConfigRestart();
209
+ return;
210
+ }
211
+ if (!/\.(md|mdx)$/i.test(normalizedPath) && normalizedPath !== "index.css" && !(normalizedPath.endsWith("/index.astro") || normalizedPath === "index.astro")) {
212
+ return;
213
+ }
214
+ queueSync();
215
+ });
216
+ console.log(`[stropress] Watching docs changes: ${input.sourceDir}`);
217
+ return () => {
218
+ watcher.close();
219
+ if (timer) {
220
+ clearTimeout(timer);
221
+ }
222
+ if (configTimer) {
223
+ clearTimeout(configTimer);
224
+ }
225
+ };
226
+ };
227
+ var copyDocsRecursive = async (sourceDir, targetDir) => {
228
+ const entries = await fs.readdir(sourceDir, { withFileTypes: true });
229
+ for (const entry of entries) {
230
+ if (entry.name === "config.json") {
231
+ continue;
232
+ }
233
+ const sourcePath = path.join(sourceDir, entry.name);
234
+ const targetPath = path.join(targetDir, entry.name);
235
+ if (entry.isDirectory()) {
236
+ await fs.ensureDir(targetPath);
237
+ await copyDocsRecursive(sourcePath, targetPath);
238
+ continue;
239
+ }
240
+ if (isSupportedDocFile(entry.name, sourcePath)) {
241
+ await fs.ensureDir(path.dirname(targetPath));
242
+ await fs.copyFile(sourcePath, targetPath);
243
+ }
244
+ }
245
+ };
246
+ var writeAstroConfig = async (input) => {
247
+ await fs.remove(path.join(input.themeDir, ".daoke"));
248
+ const runtimeDir = path.join(input.themeDir, ".stropress");
249
+ const astroConfigPath = path.join(runtimeDir, "astro.config.mjs");
250
+ const serializedConfig = JSON.stringify(input.config);
251
+ const siteUrl = resolveSiteUrl(input.config);
252
+ const codeTheme = input.config.markdown?.codeTheme;
253
+ const shikiConfig = codeTheme ? `,
254
+ shikiConfig: {
255
+ theme: ${JSON.stringify(codeTheme)}
256
+ }` : "";
257
+ await fs.ensureDir(runtimeDir);
258
+ const content = `import { defineConfig } from "astro/config";
259
+ import mdx from "@astrojs/mdx";
260
+ import remarkGithubAlerts from "remark-github-alerts";
261
+
262
+ export default defineConfig({
263
+ outDir: ${JSON.stringify(path.join(input.cwd, "dist"))},
264
+ devToolbar: {
265
+ enabled: false
266
+ },
267
+ markdown: {
268
+ remarkPlugins: [remarkGithubAlerts]${shikiConfig}
269
+ },
270
+ integrations: [
271
+ mdx({
272
+ remarkPlugins: [remarkGithubAlerts]
273
+ })
274
+ ],
275
+ site: ${JSON.stringify(siteUrl)},
276
+ vite: {
277
+ define: {
278
+ "import.meta.env.STROPRESS_SITE_CONFIG": ${JSON.stringify(serializedConfig)}
279
+ }
280
+ }
281
+ });
282
+ `;
283
+ await fs.writeFile(astroConfigPath, content, "utf8");
284
+ return astroConfigPath;
285
+ };
286
+ var resolveSiteUrl = (config) => {
287
+ const configuredUrl = config.site?.url?.trim();
288
+ if (!configuredUrl) {
289
+ return "http://localhost:4321";
290
+ }
291
+ try {
292
+ return new URL(configuredUrl).toString();
293
+ } catch {
294
+ throw new Error(`Invalid site.url in config.json: ${configuredUrl}. Expected a full URL such as https://docs.example.com`);
295
+ }
296
+ };
297
+ program.name("stropress").description("Build Astro docs from a local docs directory");
298
+ var registerCommand = (name) => {
299
+ program.command(name).option("--dir <dir>", "Directory containing docs content and config.json", "docs").action(async (options) => run(name, options));
300
+ };
301
+ registerCommand("dev");
302
+ registerCommand("build");
303
+ program.parseAsync(process.argv).catch((error) => {
304
+ const message = error instanceof Error ? error.message : String(error);
305
+ console.error(`[stropress] ${message}`);
306
+ process.exitCode = 1;
307
+ });
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@stropress/theme-default",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "dependencies": {
7
+ "@astrojs/mdx": "5.0.3",
8
+ "@fontsource/inter": "^5.2.8",
9
+ "@fontsource/jetbrains-mono": "^5.2.8",
10
+ "@lucide/astro": "^1.7.0",
11
+ "@resvg/resvg-js": "^2.6.2",
12
+ "astro": "6.1.3",
13
+ "copy-to-clipboard": "^3.3.3",
14
+ "minisearch": "^7.2.0",
15
+ "postcss-nesting": "^14.0.0",
16
+ "remark-github-alerts": "^0.1.1",
17
+ "satori": "^0.26.0",
18
+ "satori-html": "^0.3.2"
19
+ }
20
+ }
@@ -0,0 +1,5 @@
1
+ import postcssNesting from "postcss-nesting";
2
+
3
+ export default {
4
+ plugins: [postcssNesting()],
5
+ };
@@ -0,0 +1,53 @@
1
+ ---
2
+ interface Props {
3
+ title: string;
4
+ description: string;
5
+ lang?: string;
6
+ canonical?: string;
7
+ image?: string;
8
+ noindex?: boolean;
9
+ ogType?: "website" | "article";
10
+ }
11
+
12
+ const {
13
+ title,
14
+ description,
15
+ lang = "en-US",
16
+ canonical,
17
+ image,
18
+ noindex = false,
19
+ ogType = "website",
20
+ } = Astro.props;
21
+
22
+ const site = Astro.site;
23
+ const canonicalUrl =
24
+ canonical ||
25
+ (site ? new URL(Astro.url.pathname, site).toString() : undefined);
26
+ const imageUrl = image
27
+ ? site
28
+ ? new URL(image, site).toString()
29
+ : image
30
+ : undefined;
31
+ const locale = lang.replace("-", "_");
32
+ const twitterCard = imageUrl ? "summary_large_image" : "summary";
33
+ ---
34
+
35
+ <meta charset="UTF-8" />
36
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
37
+ <title>{title}</title>
38
+ <meta name="description" content={description} />
39
+ <meta name="robots" content={noindex ? "noindex, nofollow" : "index, follow"} />
40
+
41
+ {canonicalUrl && <link rel="canonical" href={canonicalUrl} />}
42
+
43
+ <meta property="og:type" content={ogType} />
44
+ <meta property="og:title" content={title} />
45
+ <meta property="og:description" content={description} />
46
+ <meta property="og:locale" content={locale} />
47
+ {canonicalUrl && <meta property="og:url" content={canonicalUrl} />}
48
+ {imageUrl && <meta property="og:image" content={imageUrl} />}
49
+
50
+ <meta name="twitter:card" content={twitterCard} />
51
+ <meta name="twitter:title" content={title} />
52
+ <meta name="twitter:description" content={description} />
53
+ {imageUrl && <meta name="twitter:image" content={imageUrl} />}
@@ -0,0 +1,111 @@
1
+ ---
2
+ interface TocItem {
3
+ depth: number;
4
+ slug: string;
5
+ text: string;
6
+ }
7
+
8
+ interface Props {
9
+ items?: TocItem[];
10
+ }
11
+
12
+ const { items = [] } = Astro.props;
13
+ ---
14
+
15
+ {
16
+ items.length > 0 && (
17
+ <aside class="doc-toc" aria-label="Table of contents">
18
+ <p class="doc-toc-title">On this page</p>
19
+ <ul class="doc-toc-list">
20
+ {items.map((heading) => (
21
+ <li
22
+ class:list={["doc-toc-item", `depth-${heading.depth}`]}
23
+ data-target-id={heading.slug}
24
+ data-state="inactive"
25
+ >
26
+ <a href={`#${heading.slug}`} class="doc-toc-link">
27
+ {heading.text}
28
+ </a>
29
+ </li>
30
+ ))}
31
+ </ul>
32
+ </aside>
33
+ )
34
+ }
35
+
36
+ <script>
37
+ import { setupDocToc } from "../scripts/doc-toc";
38
+
39
+ setupDocToc();
40
+ </script>
41
+
42
+ <style is:global>
43
+ .doc-toc {
44
+ display: none;
45
+ }
46
+
47
+ .doc-toc-title {
48
+ margin: 0 0 0.9rem;
49
+ font-size: 0.78rem;
50
+ font-weight: 700;
51
+ letter-spacing: 0.06em;
52
+ text-transform: uppercase;
53
+ color: rgba(var(--muted-color) / 1);
54
+ }
55
+
56
+ .doc-toc-list {
57
+ position: relative;
58
+ margin: 0;
59
+ padding: 0;
60
+ list-style: none;
61
+ display: flex;
62
+ flex-direction: column;
63
+ gap: 0.28rem;
64
+ }
65
+
66
+ .doc-toc-list::before {
67
+ content: "";
68
+ position: absolute;
69
+ top: 0;
70
+ left: 0;
71
+ bottom: 0;
72
+ width: 2px;
73
+ background: rgba(var(--border-color) / 0.4);
74
+ }
75
+
76
+ .doc-toc-item.depth-3 {
77
+ padding-left: 0.8rem;
78
+ }
79
+
80
+ .doc-toc-item {
81
+ border-left: 2px solid transparent;
82
+ }
83
+
84
+ .doc-toc-item:hover,
85
+ .doc-toc-item[data-state="active"] {
86
+ color: rgba(var(--foreground-color) / 1);
87
+ border-color: rgba(var(--foreground-color) / 1);
88
+ }
89
+
90
+ .doc-toc-link {
91
+ display: block;
92
+ padding: 0.2rem 0.65rem;
93
+ text-decoration: none;
94
+ line-height: 1.5;
95
+ color: rgba(var(--muted-color) / 1);
96
+ transition:
97
+ color 0.14s ease,
98
+ border-color 0.14s ease;
99
+ }
100
+
101
+ @media (min-width: 1200px) {
102
+ .doc-toc {
103
+ display: block;
104
+ position: sticky;
105
+ top: 5.7rem;
106
+ max-height: calc(100vh - 6.2rem);
107
+ overflow: auto;
108
+ padding: 0.3rem 0 0;
109
+ }
110
+ }
111
+ </style>
@@ -0,0 +1,35 @@
1
+ ---
2
+ interface Props {
3
+ class?: string;
4
+ color?: string;
5
+ size?: number | string;
6
+ title?: string;
7
+ "aria-hidden"?: "true" | "false";
8
+ }
9
+
10
+ const {
11
+ class: className,
12
+ color,
13
+ size,
14
+ title,
15
+ "aria-hidden": ariaHidden,
16
+ } = Astro.props;
17
+
18
+ const resolvedSize = size ?? 20;
19
+ ---
20
+
21
+ <svg
22
+ xmlns="http://www.w3.org/2000/svg"
23
+ width={resolvedSize}
24
+ height={resolvedSize}
25
+ fill={color ?? "currentColor"}
26
+ viewBox="0 0 24 24"
27
+ class={className}
28
+ aria-hidden={ariaHidden}
29
+ role={title ? "img" : undefined}
30
+ aria-label={title}
31
+ >
32
+ <path
33
+ d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
34
+ ></path>
35
+ </svg>