@x-wave/blog 2.0.0 → 2.1.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.
- package/README.md +496 -63
- package/index.js +1194 -1270
- package/package.json +7 -4
- package/styles/index.css +2 -2
- package/vite-config/blog-discovery.d.ts +15 -0
- package/vite-config/blog-discovery.d.ts.map +1 -0
- package/vite-config/blog-discovery.js +131 -0
- package/vite-config/blog-discovery.js.map +1 -0
- package/vite-config/index.d.ts +5 -0
- package/vite-config/index.d.ts.map +1 -0
- package/vite-config/index.js +9 -0
- package/vite-config/index.js.map +1 -0
- package/vite-config/meta-tags.d.ts +28 -0
- package/vite-config/meta-tags.d.ts.map +1 -0
- package/vite-config/meta-tags.js +129 -0
- package/vite-config/meta-tags.js.map +1 -0
- package/vite-config/setup-ssg.d.ts +41 -0
- package/vite-config/setup-ssg.d.ts.map +1 -0
- package/vite-config/setup-ssg.js +84 -0
- package/vite-config/setup-ssg.js.map +1 -0
- package/vite-config/static-gen-plugin.d.ts +19 -0
- package/vite-config/static-gen-plugin.d.ts.map +1 -0
- package/vite-config/static-gen-plugin.js +56 -0
- package/vite-config/static-gen-plugin.js.map +1 -0
- package/vite-config/types.d.ts +30 -0
- package/vite-config/types.d.ts.map +1 -0
- package/vite-config/types.js +5 -0
- package/vite-config/types.js.map +1 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { discoverBlogPosts } from './blog-discovery.js';
|
|
4
|
+
import { createStaticGenPlugin } from './static-gen-plugin.js';
|
|
5
|
+
/**
|
|
6
|
+
* Read all MDX files from a docs directory
|
|
7
|
+
* @param docsDir - Path to the docs directory
|
|
8
|
+
* @returns Object mapping file paths to content loaders
|
|
9
|
+
*/
|
|
10
|
+
function readMdxFiles(docsDir) {
|
|
11
|
+
const mdxFiles = {};
|
|
12
|
+
function walkDir(dir, relativePath = '') {
|
|
13
|
+
if (!fs.existsSync(dir))
|
|
14
|
+
return;
|
|
15
|
+
const files = fs.readdirSync(dir);
|
|
16
|
+
for (const file of files) {
|
|
17
|
+
const fullPath = path.join(dir, file);
|
|
18
|
+
const relPath = path.join(relativePath, file);
|
|
19
|
+
const stat = fs.statSync(fullPath);
|
|
20
|
+
if (stat.isDirectory()) {
|
|
21
|
+
walkDir(fullPath, relPath);
|
|
22
|
+
}
|
|
23
|
+
else if (file.endsWith('.mdx')) {
|
|
24
|
+
// Create a lazy loader function
|
|
25
|
+
mdxFiles[path.join(docsDir, relPath)] = async () => fs.readFileSync(fullPath, 'utf-8');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
walkDir(docsDir);
|
|
30
|
+
return mdxFiles;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Setup static site generation for blog posts
|
|
34
|
+
*
|
|
35
|
+
* This is a convenience function that handles the entire SSG setup process.
|
|
36
|
+
* It discovers blog posts from your docs directory and creates a Vite plugin.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import { setupSSG } from '@x-wave/blog/vite-config'
|
|
41
|
+
*
|
|
42
|
+
* export default defineConfig(async (env) => {
|
|
43
|
+
* const ssgPlugin = env.command === 'build'
|
|
44
|
+
* ? await setupSSG({
|
|
45
|
+
* docsPath: 'src/docs',
|
|
46
|
+
* outputDir: 'dist/docs',
|
|
47
|
+
* metaTagsOptions: {
|
|
48
|
+
* baseUrl: 'https://example.com',
|
|
49
|
+
* siteName: 'My Documentation',
|
|
50
|
+
* }
|
|
51
|
+
* })
|
|
52
|
+
* : undefined
|
|
53
|
+
*
|
|
54
|
+
* return {
|
|
55
|
+
* plugins: [react(), ssgPlugin].filter(Boolean),
|
|
56
|
+
* // ... rest of config
|
|
57
|
+
* }
|
|
58
|
+
* })
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export async function setupSSG(options) {
|
|
62
|
+
try {
|
|
63
|
+
const { docsPath, outputDir = 'dist/docs', metaTagsOptions = {} } = options;
|
|
64
|
+
const mdxFiles = readMdxFiles(docsPath);
|
|
65
|
+
const { posts } = await discoverBlogPosts(mdxFiles, {
|
|
66
|
+
blogContentPath: docsPath,
|
|
67
|
+
});
|
|
68
|
+
if (posts.length === 0) {
|
|
69
|
+
console.log('📝 No blog posts found, SSG disabled');
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
console.log(`📝 Discovered ${posts.length} blog posts for SSG`);
|
|
73
|
+
return createStaticGenPlugin({
|
|
74
|
+
posts,
|
|
75
|
+
outputDir,
|
|
76
|
+
metaTagsOptions,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.warn('⚠️ SSG initialization failed:', error);
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=setup-ssg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-ssg.js","sourceRoot":"","sources":["../src/setup-ssg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAEvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAW9D;;;;GAIG;AACH,SAAS,YAAY,CAAC,OAAe;IACpC,MAAM,QAAQ,GAA0C,EAAE,CAAA;IAE1D,SAAS,OAAO,CAAC,GAAW,EAAE,YAAY,GAAG,EAAE;QAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAM;QAE/B,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;QAEjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;YAC7C,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;YAElC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YAC3B,CAAC;iBAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,gCAAgC;gBAChC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,CAClD,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YACpC,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,CAAC,OAAO,CAAC,CAAA;IAChB,OAAO,QAAQ,CAAA;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC7B,OAAwB;IAExB,IAAI,CAAC;QACJ,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,WAAW,EAAE,eAAe,GAAG,EAAE,EAAE,GAAG,OAAO,CAAA;QAE3E,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAA;QACvC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE;YACnD,eAAe,EAAE,QAAQ;SACzB,CAAC,CAAA;QAEF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAA;YACnD,OAAO,SAAS,CAAA;QACjB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,CAAC,MAAM,qBAAqB,CAAC,CAAA;QAE/D,OAAO,qBAAqB,CAAC;YAC5B,KAAK;YACL,SAAS;YACT,eAAe;SACf,CAAC,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAA;QACrD,OAAO,SAAS,CAAA;IACjB,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
import { type MetaTagsOptions } from './meta-tags.js';
|
|
3
|
+
import type { BlogPostMetadata, BlogSSGConfig } from './types.js';
|
|
4
|
+
export interface StaticGenPluginOptions extends BlogSSGConfig {
|
|
5
|
+
/** Blog posts to generate static pages for */
|
|
6
|
+
posts: BlogPostMetadata[];
|
|
7
|
+
/** Meta tag generation options */
|
|
8
|
+
metaTagsOptions?: MetaTagsOptions;
|
|
9
|
+
/** Reference to original index.html path */
|
|
10
|
+
indexHtmlPath?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Vite plugin for static site generation of blog posts
|
|
14
|
+
*
|
|
15
|
+
* Generates individual HTML files for each blog post with injected meta tags
|
|
16
|
+
* These files are created after the main build completes
|
|
17
|
+
*/
|
|
18
|
+
export declare function createStaticGenPlugin(options: StaticGenPluginOptions): Plugin;
|
|
19
|
+
//# sourceMappingURL=static-gen-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-gen-plugin.d.ts","sourceRoot":"","sources":["../src/static-gen-plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEjE,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC5D,8CAA8C;IAC9C,KAAK,EAAE,gBAAgB,EAAE,CAAA;IACzB,kCAAkC;IAClC,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CA4E7E"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { generateHtmlWithMetaTags } from './meta-tags.js';
|
|
4
|
+
/**
|
|
5
|
+
* Vite plugin for static site generation of blog posts
|
|
6
|
+
*
|
|
7
|
+
* Generates individual HTML files for each blog post with injected meta tags
|
|
8
|
+
* These files are created after the main build completes
|
|
9
|
+
*/
|
|
10
|
+
export function createStaticGenPlugin(options) {
|
|
11
|
+
let config;
|
|
12
|
+
const { posts, outputDir = 'dist/docs', metaTagsOptions = {}, indexHtmlPath = 'index.html', } = options;
|
|
13
|
+
return {
|
|
14
|
+
name: 'vite-plugin-blog-static-gen',
|
|
15
|
+
configResolved(resolvedConfig) {
|
|
16
|
+
config = resolvedConfig;
|
|
17
|
+
},
|
|
18
|
+
async generateBundle() {
|
|
19
|
+
// This hook runs during the bundle generation phase
|
|
20
|
+
// We'll actually write files in writeBundle to ensure index.html exists
|
|
21
|
+
},
|
|
22
|
+
async writeBundle() {
|
|
23
|
+
if (!posts || posts.length === 0) {
|
|
24
|
+
console.log('📝 No blog posts to generate static files for');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
// Read the generated index.html
|
|
29
|
+
const indexHtmlPath_ = path.resolve(config.build.outDir, 'index.html');
|
|
30
|
+
if (!fs.existsSync(indexHtmlPath_)) {
|
|
31
|
+
console.warn(`⚠️ Index HTML not found at ${indexHtmlPath_}, skipping static generation`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const indexHtmlContent = fs.readFileSync(indexHtmlPath_, 'utf-8');
|
|
35
|
+
console.log(`📝 Generating static pages for ${posts.length} blog posts...`);
|
|
36
|
+
// Create static HTML file for each blog post
|
|
37
|
+
for (const post of posts) {
|
|
38
|
+
const htmlWithMeta = generateHtmlWithMetaTags(indexHtmlContent, post, metaTagsOptions);
|
|
39
|
+
// Create directory structure: dist/docs/[language]/[slug]/index.html
|
|
40
|
+
const postDir = path.resolve(config.build.outDir, post.language, post.slug);
|
|
41
|
+
fs.mkdirSync(postDir, { recursive: true });
|
|
42
|
+
// Write the static HTML file
|
|
43
|
+
const htmlPath = path.resolve(postDir, 'index.html');
|
|
44
|
+
fs.writeFileSync(htmlPath, htmlWithMeta, 'utf-8');
|
|
45
|
+
console.log(` ✓ Generated ${post.language}/${post.slug}/`);
|
|
46
|
+
}
|
|
47
|
+
console.log(`✨ Static generation complete for ${posts.length} blog posts`);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error('❌ Error generating static blog posts:', error);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=static-gen-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-gen-plugin.js","sourceRoot":"","sources":["../src/static-gen-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,wBAAwB,EAAwB,MAAM,gBAAgB,CAAA;AAY/E;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAA+B;IACpE,IAAI,MAAW,CAAA;IACf,MAAM,EACL,KAAK,EACL,SAAS,GAAG,WAAW,EACvB,eAAe,GAAG,EAAE,EACpB,aAAa,GAAG,YAAY,GAC5B,GAAG,OAAO,CAAA;IAEX,OAAO;QACN,IAAI,EAAE,6BAA6B;QAEnC,cAAc,CAAC,cAAc;YAC5B,MAAM,GAAG,cAAc,CAAA;QACxB,CAAC;QAED,KAAK,CAAC,cAAc;YACnB,oDAAoD;YACpD,wEAAwE;QACzE,CAAC;QAED,KAAK,CAAC,WAAW;YAChB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAClC,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAA;gBAC5D,OAAM;YACP,CAAC;YAED,IAAI,CAAC;gBACJ,gCAAgC;gBAChC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;gBACtE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;oBACpC,OAAO,CAAC,IAAI,CACX,+BAA+B,cAAc,8BAA8B,CAC3E,CAAA;oBACD,OAAM;gBACP,CAAC;gBAED,MAAM,gBAAgB,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAA;gBAEjE,OAAO,CAAC,GAAG,CACV,kCAAkC,KAAK,CAAC,MAAM,gBAAgB,CAC9D,CAAA;gBAED,6CAA6C;gBAC7C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBAC1B,MAAM,YAAY,GAAG,wBAAwB,CAC5C,gBAAgB,EAChB,IAAI,EACJ,eAAe,CACf,CAAA;oBAED,qEAAqE;oBACrE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAC3B,MAAM,CAAC,KAAK,CAAC,MAAM,EACnB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,IAAI,CACT,CAAA;oBAED,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;oBAE1C,6BAA6B;oBAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,YAAY,CAAC,CAAA;oBACpD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,CAAA;oBAEjD,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;gBAC5D,CAAC;gBAED,OAAO,CAAC,GAAG,CACV,oCAAoC,KAAK,CAAC,MAAM,aAAa,CAC7D,CAAA;YACF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAA;gBAC7D,MAAM,KAAK,CAAA;YACZ,CAAC;QACF,CAAC;KACD,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for blog SSG functionality
|
|
3
|
+
*/
|
|
4
|
+
export interface BlogPostMetadata {
|
|
5
|
+
slug: string;
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
ogImage?: string;
|
|
9
|
+
keywords?: string[] | string;
|
|
10
|
+
date?: string;
|
|
11
|
+
author?: string;
|
|
12
|
+
language: string;
|
|
13
|
+
filePath: string;
|
|
14
|
+
}
|
|
15
|
+
export interface BlogDiscoveryResult {
|
|
16
|
+
posts: BlogPostMetadata[];
|
|
17
|
+
postsByLanguage: Record<string, BlogPostMetadata[]>;
|
|
18
|
+
postsBySlug: Record<string, BlogPostMetadata>;
|
|
19
|
+
}
|
|
20
|
+
export interface BlogSSGConfig {
|
|
21
|
+
/** Path to blog MDX files relative to app root */
|
|
22
|
+
blogContentPath?: string;
|
|
23
|
+
/** Whether to generate static files */
|
|
24
|
+
generateStatic?: boolean;
|
|
25
|
+
/** Output directory for generated blog posts */
|
|
26
|
+
outputDir?: string;
|
|
27
|
+
/** Base path for blog routes */
|
|
28
|
+
basePath?: string;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAA;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,gBAAgB,EAAE,CAAA;IACzB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAA;IACnD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CAC7C;AAED,MAAM,WAAW,aAAa;IAC7B,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,uCAAuC;IACvC,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|