Skip to content

feat: SvelteKit integration (aeo.js/sveltekit)#64

Merged
rubenmarcus merged 5 commits into
mainfrom
feat/sveltekit-plugin
Jun 16, 2026
Merged

feat: SvelteKit integration (aeo.js/sveltekit)#64
rubenmarcus merged 5 commits into
mainfrom
feat/sveltekit-plugin

Conversation

@rubenmarcus

Copy link
Copy Markdown
Member

Adds the SvelteKit plugin — the types and detection already anticipated it (FrameworkType includes sveltekit, tsup externals include @sveltejs/kit); this ships the actual integration.

What

  • aeo.js/sveltekit new entry point following the Angular plugin pattern:
    • postBuild() — detects the output dir (build/ for adapter-static, .svelte-kit/output + prerendered pages for other adapters), scans prerendered HTML for content, generates all AEO files alongside the build, and injects the Human/AI widget into every prerendered page (idempotent, opt-out via injectWidget: false).
    • generate() — source-only mode scanning src/routes (+page.svelte/+page.md/+page.svx; route groups (name) transparent, dynamic [slug] segments skipped), writing into static/.
    • getWidgetScript() — same surface as the Angular plugin.
  • tsup entry + exports map wiring
  • Docs: frameworks/sveltekit guide + sidebar entry + README quick start & table row

Verification

  • tsc --noEmit clean, build succeeds with the new entry
  • 187 tests pass (7 new, real-filesystem fixtures: route scanning incl. groups/dynamic segments, adapter-static postBuild output, widget injection idempotency, non-static adapter fallback)

🤖 Generated with Claude Code

rubenmarcus and others added 2 commits June 10, 2026 13:11
aeo.js/sveltekit follows the Angular plugin pattern: postBuild() scans
the build output (adapter-static's build/, or .svelte-kit/output for
other adapters' prerendered pages), generates all AEO files alongside
it, and injects the widget into every prerendered page. generate()
discovers routes from src/routes (group segments transparent, dynamic
segments skipped) and writes into static/ for dev use.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aeo-js Ready Ready Preview, Comment Jun 16, 2026 10:30pm

Request Review

@github-actions

Copy link
Copy Markdown

Docs Preview

Preview URL: https://feat-sveltekit-plugin.aeojs.pages.dev

This preview was deployed from the latest commit on this PR.

@greptile-apps

greptile-apps Bot commented Jun 10, 2026

Copy link
Copy Markdown

Greptile Summary

Adds the aeo.js/sveltekit integration package, completing the framework matrix by following the established Angular plugin pattern. The four issues flagged in the previous review round (non-static adapter widget injection, config.pages override silently dropped, </script> injection in widget config, and missing output-dir warning) are all addressed with tests.

  • postBuild detects build/ (adapter-static) or .svelte-kit/output/client (other adapters), scans prerendered HTML from both the output dir and prerendered/pages, generates AEO files into the output dir, and injects the widget script idempotently into every prerendered page.
  • generate source-only mode walks src/routes, skips dynamic [param] and transparent (group) segments, and writes AEO files to static/ — suitable for any adapter including dev mode.
  • Package wiring (exports map + tsup entry) and docs (sveltekit.mdx + sidebar + README) are consistent with existing integrations.

Confidence Score: 5/5

Safe to merge — the new integration is well-tested with real-filesystem fixtures and all previously reported correctness issues have been addressed.

The plugin is a net-new entry point that doesn't touch any shared runtime paths. All four previously flagged correctness issues are fixed and covered by new tests. The one remaining finding is a minor inconsistency in generate where config.title fallback is applied before rather than after the config.pages override, affecting only callers who pass both fields simultaneously without a title on the root page entry.

src/plugins/sveltekit.ts — the title/description fallback ordering in generate is worth a second look.

Important Files Changed

Filename Overview
src/plugins/sveltekit.ts New SvelteKit integration — all four previously reported issues (widget injection path for non-static adapters, config.pages override, </script> escaping, fallback dir warning) are now addressed; one minor inconsistency remains between generate and postBuild in when the config.title/description fallback is applied relative to config.pages overrides.
src/plugins/sveltekit.test.ts New test suite with real-filesystem fixtures covering route scanning, adapter-static postBuild, widget injection idempotency, non-static adapter fallback, config.pages priority, and </script> XSS escaping — good coverage of all previously reported edge cases.
package.json Adds the ./sveltekit exports map entry (types + ESM + CJS) consistent with the existing angular, nuxt, and other plugin entries.
tsup.config.ts Single-line addition of the sveltekit entry point, consistent with existing plugin registrations.
website/src/content/docs/frameworks/sveltekit.mdx New docs page for the SvelteKit integration covering setup, how-it-works, programmatic usage, and configuration options — structure matches other framework docs pages.
README.md Adds SvelteKit quick-start snippet and a table row for the new entry point — consistent with existing framework rows.
website/astro.config.mjs Adds SvelteKit sidebar entry between Nuxt and Angular — maintains the existing ordering convention.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph postBuild["postBuild(config)"]
        PD[detectSvelteKitOutputDir] --> PS[scanHtmlOutput - build/ or client/]
        PS --> PP[scanHtmlOutput - prerendered/pages]
        PP --> PSR[scanSvelteKitRoutes - src/routes]
        PSR --> PM["Merge: discovered > source > config.pages wins"]
        PM --> PF[Apply config.title/description fallbacks]
        PF --> PG[generateAEOFiles → outDir]
        PG --> PI{injectWidget?}
        PI -->|yes| PW[injectWidgetIntoHtml outDir + prerenderedDir]
        PI -->|no| PE[done]
        PW --> PE
    end

    subgraph generate["generate(config)"]
        GS[scanSvelteKitRoutes - src/routes] --> GM["Build pageMap: discovered, then config.pages wins"]
        GM --> GF["Apply config.title/description fallbacks before override"]
        GF --> GG[generateAEOFiles → static/]
    end

    subgraph getWidgetScript["getWidgetScript(config)"]
        WR[resolveConfig] --> WE{widget.enabled?}
        WE -->|no| WN[return empty string]
        WE -->|yes| WJ["JSON.stringify + escape < > &"]
        WJ --> WT[return script tag]
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    subgraph postBuild["postBuild(config)"]
        PD[detectSvelteKitOutputDir] --> PS[scanHtmlOutput - build/ or client/]
        PS --> PP[scanHtmlOutput - prerendered/pages]
        PP --> PSR[scanSvelteKitRoutes - src/routes]
        PSR --> PM["Merge: discovered > source > config.pages wins"]
        PM --> PF[Apply config.title/description fallbacks]
        PF --> PG[generateAEOFiles → outDir]
        PG --> PI{injectWidget?}
        PI -->|yes| PW[injectWidgetIntoHtml outDir + prerenderedDir]
        PI -->|no| PE[done]
        PW --> PE
    end

    subgraph generate["generate(config)"]
        GS[scanSvelteKitRoutes - src/routes] --> GM["Build pageMap: discovered, then config.pages wins"]
        GM --> GF["Apply config.title/description fallbacks before override"]
        GF --> GG[generateAEOFiles → static/]
    end

    subgraph getWidgetScript["getWidgetScript(config)"]
        WR[resolveConfig] --> WE{widget.enabled?}
        WE -->|no| WN[return empty string]
        WE -->|yes| WJ["JSON.stringify + escape < > &"]
        WJ --> WT[return script tag]
    end
Loading

Reviews (7): Last reviewed commit: "fix(sveltekit): warn when output directo..." | Re-trigger Greptile

Comment thread src/plugins/sveltekit.ts
Comment thread src/plugins/sveltekit.ts Outdated
Comment thread src/plugins/sveltekit.ts Outdated
Comment thread src/plugins/sveltekit.ts
…script tag escaping

- Widget injection for non-static adapters now scans .svelte-kit/output/prerendered/pages (not just the client dir which holds only JS/CSS bundles)
- config.pages entries now take highest merge priority — they always overwrite auto-discovered pages with the same pathname instead of being silently dropped
- JSON config serialized into the <script> block now escapes <, >, and & as unicode escapes to prevent </script> in config values from prematurely closing the script element

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rubenmarcus

Copy link
Copy Markdown
Member Author

@greptileai review

@rubenmarcus

Copy link
Copy Markdown
Member Author

@greptileai review

…rate() pages

- Replace outputDir.includes('.svelte-kit') with existsSync(prerenderedDir) so
  the non-static adapter path works even when the caller sets a custom outDir.
- Apply Map-based deduplication in generate() so config.pages always wins over
  auto-scanned routes, matching the postBuild() merge behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rubenmarcus

Copy link
Copy Markdown
Member Author

@greptileai review

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rubenmarcus

Copy link
Copy Markdown
Member Author

@greptile review

@rubenmarcus rubenmarcus merged commit 2dd6e49 into main Jun 16, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant