aeo.js 0.0.8 → 0.0.9

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 CHANGED
@@ -1,39 +1,22 @@
1
- # aeo.js
2
-
3
- Answer Engine Optimization for the modern web. Make your site discoverable by AI crawlers and LLMs.
4
-
5
1
  <p align="center">
6
- <a href="https://ralphstarter.ai/badge"><img src="https://ralphstarter.ai/img/badge-built-with@2x.png" alt="built with ralph-starter" height="28"></a>
2
+ <h1 align="center">aeo.js</h1>
3
+ <p align="center">Answer Engine Optimization for the modern web.<br/>Make your site discoverable by ChatGPT, Claude, Perplexity, and every AI answer engine.</p>
7
4
  </p>
8
5
 
9
- ## What is AEO?
10
-
11
- Answer Engine Optimization (AEO) is the practice of making your website discoverable and citable by AI-powered answer engines like ChatGPT, Claude, Perplexity, and SearchGPT.
12
-
13
- aeo.js auto-generates the files these engines look for and provides a drop-in widget that shows visitors how your site appears to AI.
14
-
15
- ## Features
16
-
17
- - **`llms.txt` / `llms-full.txt`** -- LLM-readable summaries of your site
18
- - **`robots.txt`** -- AI-crawler-aware robots directives
19
- - **`sitemap.xml`** -- Standard sitemap generation
20
- - **`docs.json`** -- Structured documentation manifest
21
- - **`ai-index.json`** -- AI-optimized content index
22
- - **Raw Markdown** -- Per-page `.md` files extracted from your HTML
23
- - **Human/AI Widget** -- Drop-in toggle showing the AI-readable version of any page
24
- - **CLI** -- `npx aeo.js generate` to run standalone
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/aeo.js"><img src="https://img.shields.io/npm/v/aeo.js?style=flat&colorA=0d0d0d&colorB=1a1a1a" alt="npm version"></a>
8
+ <a href="https://www.npmjs.com/package/aeo.js"><img src="https://img.shields.io/npm/dm/aeo.js?style=flat&colorA=0d0d0d&colorB=1a1a1a" alt="npm downloads"></a>
9
+ <a href="https://github.com/multivmlabs/aeo.js"><img src="https://img.shields.io/github/stars/multivmlabs/aeo.js?style=flat&colorA=0d0d0d&colorB=1a1a1a" alt="GitHub stars"></a>
10
+ <a href="https://github.com/multivmlabs/aeo.js/blob/main/LICENSE"><img src="https://img.shields.io/github/license/multivmlabs/aeo.js?style=flat&colorA=0d0d0d&colorB=1a1a1a" alt="License"></a>
11
+ </p>
25
12
 
26
- ## Supported Frameworks
13
+ <p align="center">
14
+ <a href="https://aeojs.org">Documentation</a> · <a href="https://check.aeojs.org">AEO Checker</a> · <a href="https://www.npmjs.com/package/aeo.js">npm</a>
15
+ </p>
27
16
 
28
- | Framework | Status | Import |
29
- |-----------|--------|--------|
30
- | Astro | Stable | `aeo.js/astro` |
31
- | Next.js | Stable | `aeo.js/next` |
32
- | Vite / React | Stable | `aeo.js/vite` |
33
- | Nuxt | Stable | `aeo.js/nuxt` |
34
- | Angular | Stable | `aeo.js/angular` |
35
- | Webpack | Stable | `aeo.js/webpack` |
36
- | Any (CLI) | Stable | `npx aeo.js generate` |
17
+ <p align="center">
18
+ <img src="example.gif" alt="aeo.js in action" width="700">
19
+ </p>
37
20
 
38
21
  ## Install
39
22
 
@@ -62,12 +45,8 @@ export default defineConfig({
62
45
  });
63
46
  ```
64
47
 
65
- The widget is automatically injected and persists across View Transitions.
66
-
67
48
  ### Next.js
68
49
 
69
- Wrap your Next.js config with `withAeo`:
70
-
71
50
  ```js
72
51
  // next.config.mjs
73
52
  import { withAeo } from 'aeo.js/next';
@@ -81,7 +60,7 @@ export default withAeo({
81
60
  });
82
61
  ```
83
62
 
84
- After building, run the post-build step to extract content from pre-rendered pages:
63
+ Add the post-build step to `package.json`:
85
64
 
86
65
  ```json
87
66
  {
@@ -91,7 +70,7 @@ After building, run the post-build step to extract content from pre-rendered pag
91
70
  }
92
71
  ```
93
72
 
94
- ### Vite (React, Vue, Svelte, etc.)
73
+ ### Vite
95
74
 
96
75
  ```js
97
76
  // vite.config.ts
@@ -109,16 +88,8 @@ export default defineConfig({
109
88
  });
110
89
  ```
111
90
 
112
- The Vite plugin:
113
- - Generates AEO files on `vite dev` and `vite build`
114
- - Injects the widget automatically
115
- - Serves dynamic `.md` files in dev (extracts content from your running app)
116
- - Detects SPA shells and falls back to client-side DOM extraction
117
-
118
91
  ### Nuxt
119
92
 
120
- Add the module to your Nuxt config:
121
-
122
93
  ```ts
123
94
  // nuxt.config.ts
124
95
  export default defineNuxtConfig({
@@ -131,17 +102,8 @@ export default defineNuxtConfig({
131
102
  });
132
103
  ```
133
104
 
134
- The Nuxt module:
135
- - Scans your `pages/` directory for routes
136
- - Generates AEO files during dev and production builds
137
- - Scans pre-rendered HTML from `.output/public/` for full page content
138
- - Injects the widget as a client-side Nuxt plugin
139
- - Adds `<link>` and `<meta>` tags for AEO discoverability
140
-
141
105
  ### Angular
142
106
 
143
- Add a post-build step to your `package.json`:
144
-
145
107
  ```json
146
108
  {
147
109
  "scripts": {
@@ -150,20 +112,6 @@ Add a post-build step to your `package.json`:
150
112
  }
151
113
  ```
152
114
 
153
- The Angular plugin:
154
- - Reads `angular.json` to auto-detect the output directory (`dist/<project>/browser/`)
155
- - Scans route config files (`*.routes.ts`) and component directories for routes
156
- - Scans pre-rendered HTML from the build output for full page content
157
- - Injects the widget into `index.html` automatically
158
-
159
- You can also generate AEO files from source routes without building:
160
-
161
- ```ts
162
- import { generate } from 'aeo.js/angular';
163
-
164
- await generate({ title: 'My App', url: 'https://myapp.com' });
165
- ```
166
-
167
115
  ### Webpack
168
116
 
169
117
  ```js
@@ -181,101 +129,39 @@ module.exports = {
181
129
  };
182
130
  ```
183
131
 
184
- ## CLI
132
+ ### CLI
185
133
 
186
- Run aeo.js from the command line without any framework integration:
134
+ No framework needed run standalone:
187
135
 
188
136
  ```bash
189
- # Generate all AEO files
190
- npx aeo.js generate
191
-
192
- # Generate with options
193
- npx aeo.js generate --url https://mysite.com --title "My Site" --out public
194
-
195
- # Create a config file
137
+ npx aeo.js generate --url https://mysite.com --title "My Site"
196
138
  npx aeo.js init
197
-
198
- # Check your setup
199
139
  npx aeo.js check
200
140
  ```
201
141
 
202
- ### Commands
203
-
204
- | Command | Description |
205
- |---------|-------------|
206
- | `generate` | Generate all AEO files (robots.txt, llms.txt, sitemap.xml, etc.) |
207
- | `init` | Create an `aeo.config.ts` configuration file |
208
- | `check` | Validate your AEO setup and show what would be generated |
209
-
210
- ### Options
211
-
212
- | Flag | Description |
213
- |------|-------------|
214
- | `--out <dir>` | Output directory (default: auto-detected) |
215
- | `--url <url>` | Site URL |
216
- | `--title <title>` | Site title |
217
- | `--no-widget` | Disable widget generation |
218
- | `--help`, `-h` | Show help |
219
- | `--version`, `-v` | Show version |
220
-
221
- ## Configuration
222
-
223
- All framework plugins accept the same config object. You can also use `defineConfig` for standalone configs:
224
-
225
- ```js
226
- import { defineConfig } from 'aeo.js';
227
-
228
- export default defineConfig({
229
- // Required
230
- title: 'My Site',
231
- url: 'https://mysite.com',
232
-
233
- // Optional
234
- description: 'A description of your site',
235
- contentDir: 'docs', // Directory with handwritten .md files
236
- outDir: 'public', // Output directory for generated files
142
+ ## Supported Frameworks
237
143
 
238
- // Toggle individual generators
239
- generators: {
240
- robotsTxt: true, // robots.txt
241
- llmsTxt: true, // llms.txt
242
- llmsFullTxt: true, // llms-full.txt
243
- rawMarkdown: true, // Per-page .md files
244
- manifest: true, // docs.json
245
- sitemap: true, // sitemap.xml
246
- aiIndex: true, // ai-index.json
247
- },
144
+ | Framework | Import |
145
+ |-----------|--------|
146
+ | Astro | `aeo.js/astro` |
147
+ | Next.js | `aeo.js/next` |
148
+ | Vite | `aeo.js/vite` |
149
+ | Nuxt | `aeo.js/nuxt` |
150
+ | Angular | `aeo.js/angular` |
151
+ | Webpack | `aeo.js/webpack` |
152
+ | CLI | `npx aeo.js generate` |
248
153
 
249
- // Customize robots.txt
250
- robots: {
251
- allow: ['/'],
252
- disallow: ['/admin'],
253
- crawlDelay: 0,
254
- },
154
+ ## Widget
255
155
 
256
- // Widget configuration
257
- widget: {
258
- enabled: true,
259
- position: 'bottom-right', // 'bottom-left' | 'top-right' | 'top-left'
260
- humanLabel: 'Human',
261
- aiLabel: 'AI',
262
- showBadge: true,
263
- theme: {
264
- background: 'rgba(18, 18, 24, 0.9)',
265
- text: '#C0C0C5',
266
- accent: '#E8E8EA',
267
- badge: '#4ADE80',
268
- },
269
- },
270
- });
271
- ```
156
+ The Human/AI widget lets visitors toggle between the normal page and its AI-readable markdown version.
272
157
 
273
- ## Widget
158
+ | Default | Small | Icon |
159
+ |---------|-------|------|
160
+ | <img src="widget-default.gif" alt="Default widget" width="220"> | <img src="widget-small.gif" alt="Small widget" width="220"> | <img src="widget-icon.gif" alt="Icon widget" width="220"> |
274
161
 
275
- The Human/AI widget is a floating toggle that lets visitors switch between the normal page and its AI-readable markdown version. Framework plugins (Astro, Vite, Nuxt, Angular) inject it automatically. For Next.js or manual setups:
162
+ Framework plugins inject it automatically. For Next.js or manual setups:
276
163
 
277
164
  ```tsx
278
- // app/layout.tsx (or any client component)
279
165
  'use client';
280
166
  import { useEffect } from 'react';
281
167
 
@@ -295,29 +181,98 @@ export function AeoWidgetLoader() {
295
181
  }
296
182
  ```
297
183
 
298
- When a visitor clicks **AI**, the widget:
299
- 1. Fetches the `.md` file for the current page
300
- 2. Falls back to extracting markdown from the live DOM if no `.md` exists
301
- 3. Displays the markdown in a slide-out panel
302
- 4. Offers copy-to-clipboard and download actions
184
+ React and Vue wrapper components are also available:
185
+
186
+ ```tsx
187
+ import { AeoReactWidget } from 'aeo.js/react';
188
+
189
+ <AeoReactWidget config={{ title: 'My Site', url: 'https://mysite.com' }} />
190
+ ```
191
+
192
+ ```vue
193
+ <script setup>
194
+ import { AeoVueWidget } from 'aeo.js/vue';
195
+ </script>
196
+
197
+ <template>
198
+ <AeoVueWidget :config="{ title: 'My Site', url: 'https://mysite.com' }" />
199
+ </template>
200
+ ```
303
201
 
304
202
  ## Generated Files
305
203
 
306
- After building, your output directory will contain:
204
+ After building, your output directory contains:
307
205
 
308
206
  ```
309
207
  public/
310
- robots.txt # AI-crawler-aware directives
311
- llms.txt # Short LLM-readable summary
312
- llms-full.txt # Full content for LLMs
313
- sitemap.xml # Standard sitemap
314
- docs.json # Documentation manifest
315
- ai-index.json # AI-optimized content index
316
- index.md # Markdown for /
317
- about.md # Markdown for /about
318
- ... # One .md per discovered page
208
+ ├── robots.txt # AI-crawler directives
209
+ ├── llms.txt # Short LLM-readable summary
210
+ ├── llms-full.txt # Full content for LLMs
211
+ ├── sitemap.xml # Standard sitemap
212
+ ├── docs.json # Documentation manifest
213
+ ├── ai-index.json # AI content index
214
+ ├── index.md # Markdown for /
215
+ └── about.md # Markdown for /about
216
+ ```
217
+
218
+ ## Configuration
219
+
220
+ ```js
221
+ import { defineConfig } from 'aeo.js';
222
+
223
+ export default defineConfig({
224
+ title: 'My Site',
225
+ url: 'https://mysite.com',
226
+ description: 'A description of your site',
227
+
228
+ generators: {
229
+ robotsTxt: true,
230
+ llmsTxt: true,
231
+ llmsFullTxt: true,
232
+ rawMarkdown: true,
233
+ sitemap: true,
234
+ aiIndex: true,
235
+ schema: true,
236
+ },
237
+
238
+ schema: {
239
+ enabled: true,
240
+ organization: { name: 'My Company', url: 'https://mysite.com' },
241
+ defaultType: 'WebPage',
242
+ },
243
+
244
+ og: {
245
+ enabled: true,
246
+ image: 'https://mysite.com/og.png',
247
+ twitterHandle: '@mycompany',
248
+ },
249
+
250
+ widget: {
251
+ enabled: true,
252
+ position: 'bottom-right',
253
+ theme: { accent: '#4ADE80', badge: '#4ADE80' },
254
+ },
255
+ });
319
256
  ```
320
257
 
258
+ Full configuration reference → [aeojs.org/reference/configuration](https://aeojs.org/reference/configuration/)
259
+
260
+ ## Why AEO?
261
+
262
+ - **58% of searches** end without a click — AI gives the answer directly
263
+ - **40% of Gen Z** prefer AI assistants over traditional search engines
264
+ - **97% of sites** have no `llms.txt` or structured data for AI crawlers
265
+ - **1 minute** to set up with aeo.js
266
+
267
+ If your site isn't optimized for AI engines, you're invisible to a growing share of users who never open a search results page.
268
+
269
+ ## Links
270
+
271
+ - [Documentation](https://aeojs.org)
272
+ - [AEO Checker](https://check.aeojs.org)
273
+ - [npm](https://www.npmjs.com/package/aeo.js)
274
+ - [GitHub](https://github.com/multivmlabs/aeo.js)
275
+
321
276
  ## License
322
277
 
323
278
  MIT
package/dist/angular.js CHANGED
@@ -283,7 +283,8 @@ function collectMarkdownFiles(dir, base = dir) {
283
283
  for (const entry of entries) {
284
284
  const fullPath = path.join(dir, entry);
285
285
  const stat = fs.statSync(fullPath);
286
- if (stat.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
286
+ const SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "public", "dist", ".next", ".nuxt", ".output", ".open-next", "coverage", "__tests__", "__mocks__"]);
287
+ if (stat.isDirectory() && !entry.startsWith(".") && !SKIP_DIRS.has(entry)) {
287
288
  files.push(...collectMarkdownFiles(fullPath, base));
288
289
  } else if (stat.isFile() && (path.extname(entry) === ".md" || path.extname(entry) === ".mdx")) {
289
290
  const content = fs.readFileSync(fullPath, "utf-8");
@@ -626,7 +627,8 @@ function collectUrls(dir, config, base = dir) {
626
627
  for (const entry of entries) {
627
628
  const fullPath = path.join(dir, entry);
628
629
  const stat = fs.statSync(fullPath);
629
- if (stat.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
630
+ const SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "public", "dist", ".next", ".nuxt", ".output", ".open-next", "coverage", "__tests__", "__mocks__"]);
631
+ if (stat.isDirectory() && !entry.startsWith(".") && !SKIP_DIRS.has(entry)) {
630
632
  urls.push(...collectUrls(fullPath, config, base));
631
633
  } else if (stat.isFile() && (path.extname(entry) === ".md" || path.extname(entry) === ".mdx" || path.extname(entry) === ".html")) {
632
634
  const relativePath = path.relative(base, fullPath);