markopress 0.0.15 → 0.0.16
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/dist/config/validation.d.ts +23 -0
- package/dist/config/validation.js +1 -1
- package/dist/plugins/seo/index.d.ts +1 -1
- package/dist/plugins/seo/index.js +1 -1
- package/dist/plugins/seo/robots.d.ts +20 -0
- package/dist/plugins/seo/robots.js +1 -0
- package/dist/plugins/seo/types.d.ts +26 -0
- package/package.json +1 -1
|
@@ -77,6 +77,13 @@ export declare const MarkoPressConfigSchema: z.ZodObject<{
|
|
|
77
77
|
exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
78
78
|
transformItems: z.ZodOptional<z.ZodAny>;
|
|
79
79
|
}, z.core.$loose>>;
|
|
80
|
+
robots: z.ZodOptional<z.ZodObject<{
|
|
81
|
+
userAgent: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
82
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
83
|
+
disallow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
84
|
+
crawlDelay: z.ZodOptional<z.ZodNumber>;
|
|
85
|
+
sitemap: z.ZodOptional<z.ZodString>;
|
|
86
|
+
}, z.core.$loose>>;
|
|
80
87
|
}, z.core.$loose>>;
|
|
81
88
|
plugins: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodTuple<[z.ZodString, z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>], null>, z.ZodObject<{
|
|
82
89
|
name: z.ZodString;
|
|
@@ -158,6 +165,14 @@ export declare function validateConfig(config: unknown): {
|
|
|
158
165
|
exclude?: string[];
|
|
159
166
|
transformItems?: any;
|
|
160
167
|
};
|
|
168
|
+
robots?: {
|
|
169
|
+
[x: string]: unknown;
|
|
170
|
+
userAgent?: string | string[];
|
|
171
|
+
allow?: string[];
|
|
172
|
+
disallow?: string[];
|
|
173
|
+
crawlDelay?: number;
|
|
174
|
+
sitemap?: string;
|
|
175
|
+
};
|
|
161
176
|
};
|
|
162
177
|
plugins?: (string | [string?, Record<string, unknown>?, ...unknown[]] | {
|
|
163
178
|
name: string;
|
|
@@ -247,6 +262,14 @@ export declare function validateConfigSafe(config: unknown): {
|
|
|
247
262
|
exclude?: string[];
|
|
248
263
|
transformItems?: any;
|
|
249
264
|
};
|
|
265
|
+
robots?: {
|
|
266
|
+
[x: string]: unknown;
|
|
267
|
+
userAgent?: string | string[];
|
|
268
|
+
allow?: string[];
|
|
269
|
+
disallow?: string[];
|
|
270
|
+
crawlDelay?: number;
|
|
271
|
+
sitemap?: string;
|
|
272
|
+
};
|
|
250
273
|
};
|
|
251
274
|
plugins?: (string | [string?, Record<string, unknown>?, ...unknown[]] | {
|
|
252
275
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as o}from"zod";const
|
|
1
|
+
import{z as o}from"zod";const t=o.object({type:o.enum(["meta","link","script","base"])}).passthrough(),e=o.object({title:o.string().min(1,{message:"Site title is required"}).max(100,{message:"Site title too long"}),description:o.string().max(500,{message:"Description too long"}).optional(),base:o.string().startsWith("/",{message:"Base must start with /"}).optional(),lang:o.string().regex(/^[a-z]{2}(-[A-Z]{2})?$/,{message:"Invalid language code"}).optional(),head:o.array(t).optional()}),a=o.union([o.string(),o.object({dir:o.string().optional(),sidebar:o.boolean().optional(),toc:o.boolean().optional(),rss:o.boolean().optional(),list:o.boolean().optional()}).passthrough()]),n=o.record(o.string(),a),i=o.object({text:o.string().min(1,{message:"Nav item text is required"}),link:o.string().min(1,{message:"Nav item link is required"})}),s=o.object({text:o.string().min(1,{message:"Sidebar item text is required"}),link:o.string().min(1,{message:"Sidebar item link is required"})}),r=o.record(o.string(),o.union([o.array(s),o.object({autoGenerate:o.boolean()})])),l=o.object({navbar:o.array(i).optional(),sidebar:r.optional()}).passthrough(),p=o.object({name:o.string().regex(/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,{message:"Invalid theme name"}).refine(o=>!o.includes(".."),{message:"Theme name cannot contain path traversal"}).optional(),designSystem:o.enum(["vitepress","docusaurus","rspress"]).optional(),options:l.optional()}),g=o.object({lineNumbers:o.boolean().optional(),theme:o.object({light:o.string().optional(),dark:o.string().optional()}).optional(),markoTags:o.object({enabled:o.boolean().optional(),tagsDir:o.string().optional()}).optional()}),m=o.object({useCatchAllRoutes:o.boolean().optional(),outDir:o.string().optional(),assetsDir:o.string().optional(),sourcemap:o.boolean().optional(),minify:o.boolean().optional(),clean:o.boolean().optional()}).passthrough(),c=o.union([o.string(),o.tuple([o.string(),o.record(o.string(),o.unknown()).optional()]),o.object({name:o.string().min(1,{message:"Plugin name is required"}),options:o.record(o.string(),o.unknown()).optional()})]),u=o.object({hostname:o.string().optional(),exclude:o.array(o.string()).optional(),transformItems:o.any().optional()}).passthrough(),b=o.object({userAgent:o.union([o.string(),o.array(o.string())]).optional(),allow:o.array(o.string()).optional(),disallow:o.array(o.string()).optional(),crawlDelay:o.number().nonnegative().optional(),sitemap:o.string().optional()}).passthrough(),d=o.object({sitemap:u.optional(),robots:b.optional()}).passthrough().optional();export const MarkoPressConfigSchema=o.object({site:e,contentDir:o.string().optional(),content:n.optional(),theme:p.optional(),markdown:g.optional(),build:m.optional(),search:o.object({enabled:o.boolean().optional()}).passthrough().optional(),seo:d,plugins:o.array(c).optional()});export function validateConfig(o){return MarkoPressConfigSchema.parse(o)}export function validateConfigSafe(o){const t=MarkoPressConfigSchema.safeParse(o);return t.success?{success:!0,data:t.data}:{success:!1,errors:t.error.issues.map(o=>({path:o.path.join("."),message:o.message}))}}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { MarkoPressPlugin } from '../../plugin/types.js';
|
|
2
2
|
import type { SeoPluginFactoryOptions } from './types.js';
|
|
3
3
|
/**
|
|
4
|
-
* SEO Plugin - Generates sitemap.xml
|
|
4
|
+
* SEO Plugin - Generates sitemap.xml and robots.txt
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* // In markopress config
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{generateSitemap as o}from"./sitemap.js";export function seoPlugin(
|
|
1
|
+
import{generateSitemap as o}from"./sitemap.js";import{generateRobots as s}from"./robots.js";export function seoPlugin(t){return{name:"seo",async postBuild(e){const{config:i}=e,a=t||i.seo||{},r=i.seo;a?.sitemap||r?.sitemap||a?.robots||r?.robots?((a?.sitemap||r?.sitemap)&&await o(e,a.sitemap||r.sitemap),(a?.robots||r?.robots)&&await s(e,a.robots||r.robots,a.sitemap||r?.sitemap)):console.log("[seo] No SEO generation configured, skipping")}}}export default seoPlugin;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Robots.txt generation for SEO plugin
|
|
3
|
+
*/
|
|
4
|
+
import type { ResolvedConfig } from '../../config/types.js';
|
|
5
|
+
import type { SitemapOptions, RobotsOptions } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Post-build context for robots generation
|
|
8
|
+
*/
|
|
9
|
+
interface PostBuildContext {
|
|
10
|
+
config: ResolvedConfig;
|
|
11
|
+
outDir: string;
|
|
12
|
+
routes: Record<string, unknown>;
|
|
13
|
+
assets: unknown[];
|
|
14
|
+
allContent: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generate robots.txt from seo robots options
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateRobots(ctx: PostBuildContext, options?: RobotsOptions, sitemapOptions?: SitemapOptions): Promise<void>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{promises as t}from"fs";import{join as e}from"path";function r(t,e){if(e?.hostname)return e.hostname;const r=t.site;if(r.url)return r.url;throw Error("Robots hostname is required. Set site.url in config or provide hostname in sitemap/robots options.")}function o(t){return t&&0!==t.length?[...new Set(t.map(t=>t.trim()).filter(Boolean))]:[]}export async function generateRobots(s,n={},i){const{config:a,outDir:l}=s;try{const s=e(l,"public");await t.mkdir(s,{recursive:!0});const p=(c=n.userAgent)?Array.isArray(c)?c.filter(Boolean):[c]:["*"],u=o(n.allow),f=o(n.disallow),h=[];for(const t of p){h.push("User-agent: "+t);for(const t of u)h.push("Allow: "+t);for(const t of f)h.push("Disallow: "+t);"number"==typeof n.crawlDelay&&h.push("Crawl-delay: "+n.crawlDelay),h.push("")}const m=function(t,e,o){if(e?.sitemap){const s=(t.site?.base||"/").replace(/\/$/,""),n=e.sitemap.trim();if(n.startsWith("http://")||n.startsWith("https://"))return n;const i=n.startsWith("/")?n:"/"+n;let a;try{a=r(t,o)}catch{return s&&"/"!==s?`${s}${i}`:i}return s&&"/"!==s?`${a}${s}${i}`:`${a}${i}`}if(!o)return;const s=r(t,o).replace(/\/$/,""),n=(t.site?.base||"/").replace(/\/$/,""),i="/sitemap.xml";return`${s}${n&&"/"!==n?`${n}${i}`:i}`}(a,n,i);for(m&&h.push("Sitemap: "+m);h.length>0&&""===h[h.length-1];)h.pop();const $=e(s,"robots.txt"),g=h.length>0?h.join("\n")+"\n":"User-agent: *\n";await t.writeFile($,g,"utf8"),console.log("[seo] Generated robots.txt")}catch(t){const e=t instanceof Error?t.message:t+"";console.error("[seo] Failed to generate robots.txt: "+e)}var c}
|
|
@@ -33,15 +33,41 @@ export interface SitemapOptions {
|
|
|
33
33
|
lastmodDateOnly?: boolean;
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
export interface RobotsOptions {
|
|
37
|
+
/**
|
|
38
|
+
* User agent(s) to apply rules to
|
|
39
|
+
* @default ['*']
|
|
40
|
+
*/
|
|
41
|
+
userAgent?: string | string[];
|
|
42
|
+
/**
|
|
43
|
+
* Allowed paths for matching user agents
|
|
44
|
+
*/
|
|
45
|
+
allow?: string[];
|
|
46
|
+
/**
|
|
47
|
+
* Disallowed paths for matching user agents
|
|
48
|
+
*/
|
|
49
|
+
disallow?: string[];
|
|
50
|
+
/**
|
|
51
|
+
* Optional crawl delay in seconds
|
|
52
|
+
*/
|
|
53
|
+
crawlDelay?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Explicit sitemap URL to reference in robots.txt
|
|
56
|
+
* If not provided, generated from site.url / sitemap config when available
|
|
57
|
+
*/
|
|
58
|
+
sitemap?: string;
|
|
59
|
+
}
|
|
36
60
|
/**
|
|
37
61
|
* SEO plugin configuration
|
|
38
62
|
*/
|
|
39
63
|
export interface SeoPluginConfig {
|
|
40
64
|
sitemap?: SitemapOptions;
|
|
65
|
+
robots?: RobotsOptions;
|
|
41
66
|
}
|
|
42
67
|
/**
|
|
43
68
|
* Plugin factory options
|
|
44
69
|
*/
|
|
45
70
|
export interface SeoPluginFactoryOptions {
|
|
46
71
|
sitemap?: SitemapOptions;
|
|
72
|
+
robots?: RobotsOptions;
|
|
47
73
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "markopress",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "A fast, modern static site generator built on Marko.js v6 - drop-in alternative to VitePress and Docusaurus with full content compatibility",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"static-site-generator",
|