vagejs 0.2.0 → 0.3.0
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 +9 -3
- package/dist/checks/crawlability.d.ts +34 -0
- package/dist/checks/crawlability.js +142 -0
- package/dist/checks/verify.d.ts +8 -1
- package/dist/checks/verify.js +108 -11
- package/dist/cli/index.js +6 -4
- package/dist/cli/info.d.ts +11 -0
- package/dist/cli/info.js +20 -5
- package/dist/content/images.d.ts +4 -1
- package/dist/content/images.js +36 -3
- package/dist/content/markdown.d.ts +2 -0
- package/dist/content/markdown.js +2 -0
- package/dist/content/optimize.d.ts +31 -0
- package/dist/content/optimize.js +92 -0
- package/dist/content/sanitize.d.ts +8 -1
- package/dist/content/sanitize.js +3 -3
- package/dist/core/build.js +11 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.js +22 -0
- package/dist/core/dates.d.ts +2 -0
- package/dist/core/dates.js +4 -0
- package/dist/core/pages.d.ts +28 -1
- package/dist/core/pages.js +86 -11
- package/dist/core/render.d.ts +7 -0
- package/dist/core/render.js +19 -2
- package/dist/core/site.js +43 -7
- package/dist/deploy/github-pages.js +2 -0
- package/dist/dev/guard.d.ts +2 -0
- package/dist/dev/guard.js +30 -1
- package/dist/dev/server.js +19 -3
- package/dist/discovery/feed.d.ts +21 -0
- package/dist/discovery/feed.js +68 -0
- package/dist/discovery/git-dates.d.ts +10 -0
- package/dist/discovery/git-dates.js +57 -0
- package/dist/discovery/markdown-copy.d.ts +13 -0
- package/dist/discovery/markdown-copy.js +112 -0
- package/dist/guidelines/rule-pack.json +100 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/seo/head.js +16 -0
- package/package.json +4 -1
- package/templates/starter/.github/workflows/ci.yml +2 -0
- package/templates/starter/.github/workflows/verify.yml +2 -0
- package/templates/starter/AGENTS.md +15 -7
- package/templates/starter/package.json +1 -1
package/dist/seo/head.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { VageError } from "../core/errors.js";
|
|
2
2
|
import { escapeHtml } from "../core/html.js";
|
|
3
|
+
import { feedTitle } from "../discovery/feed.js";
|
|
3
4
|
import { article, jsonLdScript, organization, website } from "./jsonld.js";
|
|
4
5
|
import { absoluteUrl } from "./urls.js";
|
|
5
6
|
export function pageMeta(site, route, frontmatter, notFound = false) {
|
|
@@ -38,6 +39,21 @@ export function headTags(site, m) {
|
|
|
38
39
|
if (image)
|
|
39
40
|
tags.push(meta("property", "og:image", image));
|
|
40
41
|
tags.push(meta("name", "twitter:card", image ? "summary_large_image" : "summary"));
|
|
42
|
+
// Markdown copy for AI tools (seo.markdownCopies), on pages that are listed.
|
|
43
|
+
if (site.seo.markdownCopies && m.canonical && m.canonical === absoluteUrl(site.site, m.route)) {
|
|
44
|
+
const href = m.route === "/" ? "/index.md" : `${m.route.slice(0, -1)}.md`;
|
|
45
|
+
tags.push({ exists: /<link[^>]+text\/markdown/i, html: `<link rel="alternate" type="text/markdown" href="${escapeHtml(href)}" />` });
|
|
46
|
+
}
|
|
47
|
+
// Feed discovery: pages in a section with a feed point readers to it.
|
|
48
|
+
for (const feed of site.site ? site.seo.feeds : []) {
|
|
49
|
+
if (!m.route.startsWith(feed.under))
|
|
50
|
+
continue;
|
|
51
|
+
const href = `${site.site}${feed.under}feed.xml`;
|
|
52
|
+
tags.push({
|
|
53
|
+
exists: /<link[^>]+application\/rss\+xml/i,
|
|
54
|
+
html: `<link rel="alternate" type="application/rss+xml" title="${escapeHtml(feedTitle(site, feed))}" href="${escapeHtml(href)}" />`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
41
57
|
const schemas = structuredData(site, m, description, image);
|
|
42
58
|
if (schemas.length) {
|
|
43
59
|
tags.push({ exists: /<script[^>]+application\/ld\+json/i, html: jsonLdScript(schemas.length === 1 ? schemas[0] : schemas) });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vagejs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "AI-native, static-first web framework: Markdown sites with built-in SEO, llms.txt, audits, security and safe deploys.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"static-site-generator",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"jsdom": "^28.1.0",
|
|
51
51
|
"marked": "^18.1.1",
|
|
52
52
|
"node-html-parser": "^9.0.5",
|
|
53
|
+
"turndown": "^7.2.4",
|
|
53
54
|
"vite": "^8.3.5",
|
|
54
55
|
"yaml": "^2.9.1",
|
|
55
56
|
"zod": "^4.6.7"
|
|
@@ -58,7 +59,9 @@
|
|
|
58
59
|
"@anthropic-ai/sdk": "^1.1.0",
|
|
59
60
|
"@types/jsdom": "^28.0.3",
|
|
60
61
|
"@types/node": "^26.6.5",
|
|
62
|
+
"@types/turndown": "^5.0.6",
|
|
61
63
|
"lighthouse": "^13.5.0",
|
|
64
|
+
"sharp": "^0.35.5",
|
|
62
65
|
"typescript": "^7.0.2",
|
|
63
66
|
"vitest": "^5.0.5"
|
|
64
67
|
},
|
|
@@ -26,7 +26,10 @@ Run `npm run check` after every change and fix all errors before finishing.
|
|
|
26
26
|
- **Add a layout:** `src/layouts/<name>.ts`, then `layout: <name>` in frontmatter. Keep `<html lang>`, one `<title>` and `</head>`.
|
|
27
27
|
- **List pages (blog index, "latest posts", a sidebar):** in a layout, `collection(pages, { under: "/blog/", sort: "date" })` from `vagejs`. Never keep a hand-written list of pages.
|
|
28
28
|
- **Give a layout its own frontmatter fields:** `export const frontmatter = z.object({ ... })` in the layout (`import { z } from "vagejs"`). Pages using it are then checked strictly: unknown or mistyped fields fail the build.
|
|
29
|
-
- **Add an image:** put it in `public/images/` and write ``. Width and height are filled in from the file; write real alt text.
|
|
29
|
+
- **Add an image:** put it in `public/images/` and write ``. Width and height are filled in from the file; write real alt text. For resized WebP copies, the owner can turn on `images: { optimize: true }` (needs `npm install --save-dev sharp`). Only images in page content are optimized, not images a layout writes.
|
|
30
|
+
- **Order a menu or sidebar:** set `order:` (lower first) and optionally a short `navTitle:` in each page's frontmatter (shown as `p.label`), then build the menu in a layout with `tree(pages, "/docs/")`. For previous/next links in the same order use `neighbors(flatten(tree(pages, "/docs/")), page.route)`. For a flat section, `collection(pages, { under: "/docs/", sort: "order", includeIndex: true })` works too. `breadcrumbs(pages, page.route)` gives the trail from the home page.
|
|
31
|
+
- **Add an RSS feed for the blog:** `seo: { feeds: [{ under: "/blog/" }] }` in `vage.config.ts` writes `/blog/feed.xml`, newest `datePublished` first (undated posts last, without a date).
|
|
32
|
+
- **Help AI tools read the site:** `seo: { markdownCopies: true }` publishes a Markdown copy of every listed page (`/docs/cli/` becomes `/docs/cli.md`), converted from the published HTML page, so it contains exactly what the page shows, and `llms.txt` links to them. `seo: { lastmod: "git" }` dates the sitemap from git history (needs full history in CI: `fetch-depth: 0`).
|
|
30
33
|
- **Rename a page:** move the file, then add `redirects: { "/old-slug/": "/new-slug/" }` to `vage.config.ts`.
|
|
31
34
|
- **Hide a page from search:** `noindex: true` (it stays published). To unpublish, use `draft: true` or delete it.
|
|
32
35
|
- **AI crawler policy:** set `seo.robots.aiTraining` / `aiSearch` only on the owner's instruction.
|
|
@@ -61,7 +64,9 @@ image: /images/og.png # og:image (made absolute using `site`)
|
|
|
61
64
|
type: article # og:type + Article JSON-LD (default: website)
|
|
62
65
|
author: Jane Doe # Article author
|
|
63
66
|
datePublished: 2026-01-31
|
|
64
|
-
dateModified: 2026-02-15 # also
|
|
67
|
+
dateModified: 2026-02-15 # also the sitemap <lastmod> (otherwise datePublished, or git with seo.lastmod: "git")
|
|
68
|
+
order: 2 # position in menus built with tree() or sort: "order" (lower first)
|
|
69
|
+
navTitle: Pricing # shorter label for menus and breadcrumbs (PageSummary.label)
|
|
65
70
|
---
|
|
66
71
|
```
|
|
67
72
|
|
|
@@ -75,7 +80,7 @@ Hacked sites are usually abused for hidden spam links and doorway pages. vagejs
|
|
|
75
80
|
- **`links.lock.json` lists the external domains the site may link to.** A link to any other domain fails `npm run check` (`link-unapproved`). Only the owner approves domains: show them `npx vage links` and let them run `npx vage links approve <domain>`. Never run `approve` with `--yes` or `--all` yourself unless the owner told you to in this conversation.
|
|
76
81
|
- Inline `style="..."` is removed from page content (`markdown.inlineStyles` is off), and `vage check` fails on links hidden with CSS (`link-hidden`). Style pages in layouts.
|
|
77
82
|
- `vage check` warns about common spam words (`spam-pattern`). Report them to the owner; don't delete or reword content you didn't write without asking.
|
|
78
|
-
- `npx vage verify` compares the live site with these files and fails on links, scripts or pages that aren't in the repository. `.github/workflows/verify.yml` runs it daily once the owner sets the `VAGE_VERIFY_URL` repository variable.
|
|
83
|
+
- `npx vage verify` compares the live site with these files and fails on links, scripts or pages that aren't in the repository. It also checks that search and AI crawlers can reach the site, that `http` and `www` addresses redirect, and warns about scripts the host or CDN injects. `.github/workflows/verify.yml` runs it daily once the owner sets the `VAGE_VERIFY_URL` repository variable.
|
|
79
84
|
- CI runs `npm audit`, and `.github/dependabot.yml` proposes dependency updates. Review them like any other change.
|
|
80
85
|
|
|
81
86
|
## Content safety
|
|
@@ -107,13 +112,16 @@ From the published pages and config (never from `public/`; a hand-written `publi
|
|
|
107
112
|
- `sitemap.xml`: indexable, self-canonical, non-draft pages. Needs `site`.
|
|
108
113
|
- `llms.txt` (+ `llms-full.txt` with `llmsTxt: { full: true }`): same pages. An emerging convention; not a guarantee that AI tools use it.
|
|
109
114
|
- Redirects: `redirects: { "/old/": "/new/" }` become noindex meta-refresh pages (dev server answers 301).
|
|
115
|
+
- With `seo.feeds`: `<section>/feed.xml` (RSS). With `seo.markdownCopies`: `/<path>.md` copies of listed pages. With `images.optimize`: resized images in `/_vage/img/`.
|
|
116
|
+
|
|
110
117
|
|
|
111
118
|
## Config
|
|
112
119
|
|
|
113
120
|
`vage.config.ts` keys: `site` (origin only, no path), `title`, `description`, `lang`,
|
|
114
|
-
`seo: { robots, sitemap, llmsTxt, structuredData }`, `redirects`, `markdown`,
|
|
115
|
-
`
|
|
116
|
-
`
|
|
121
|
+
`seo: { robots, sitemap, llmsTxt, structuredData, feeds, markdownCopies, lastmod }`, `redirects`, `markdown`,
|
|
122
|
+
`images: { optimize, widths, quality, sizes }`, `performance`, `deploy: { target, cloudflareWorker, cloudflareProject }`,
|
|
123
|
+
`security: { headers, csp, cspSources, outboundLinks, spamPatterns }`, `integrations`, `guidelines`.
|
|
124
|
+
Keys not in this list are an error; `npx vage info --json` has the full schema.
|
|
117
125
|
|
|
118
126
|
## Performance and accessibility
|
|
119
127
|
|
|
@@ -155,7 +163,7 @@ Off unless configured in `vage.config.ts` under `integrations`. Ask the site own
|
|
|
155
163
|
|
|
156
164
|
## Not implemented yet
|
|
157
165
|
|
|
158
|
-
Bing Webmaster Tools API, Netlify/Vercel adapters, CMS content sources, multi-language sites, base paths,
|
|
166
|
+
Bing Webmaster Tools API, Netlify/Vercel adapters, CMS content sources, multi-language sites, base paths, AVIF images. Do not use or document them as available. A passing `check` does not guarantee indexing, rankings or AI citations.
|
|
159
167
|
|
|
160
168
|
## Favicon
|
|
161
169
|
|