@stackql/docusaurus-plugin-aeo 0.1.0 → 0.1.2
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
Fix: cross-plugin loaded content was not captured because contentLoaded does not receive allContent in Docusaurus 3.x. Use allContentLoaded hook. Without this fix, feature 1 (.md companions) emitted zero files and feature 2 (llms.txt / llms-full.txt) was empty.
|
|
6
|
+
|
|
7
|
+
## 0.1.1
|
|
8
|
+
|
|
9
|
+
Bugfix release. v0.1.0 failed to build on a real Docusaurus 3.10 consumer with three distinct crashes; all three are fixed here.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Plugin construction crash: `plugin.options.id` is `undefined`.** v0.1.0 exported a `validateOptions` function that bypassed Docusaurus's option normalization, so the standard `id: 'default'` default was never applied. `@docusaurus/core` then crashed in `lib/server/plugins/actions.js` at `createPluginActionsUtils`:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string.
|
|
17
|
+
Received undefined
|
|
18
|
+
at Object.join (node:path:460:7)
|
|
19
|
+
at createPluginActionsUtils (.../@docusaurus/core/lib/server/plugins/actions.js:27:36)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Fix: removed the `validateOptions` export so Docusaurus's built-in plugin schema runs. The plugin's handwritten construction-time validator (`normalizeOptions` + the internal `validateOptions` in `src/index.js`) still validates plugin-specific options.
|
|
23
|
+
|
|
24
|
+
- **SSR stack overflow on every doc and blog page.** The footer wrappers at `src/theme/DocItem/Footer/index.jsx` and `src/theme/BlogPostItem/Footer/index.jsx` imported `@theme-original/DocItem/Footer` and `@theme-original/BlogPostItem/Footer`. When a *plugin* (not a *theme*) contributes the wrapper and is the only contributor in the wrapper layer, `@theme-original/X` resolves back to the wrapper itself, recursing during SSG:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
Error: Can't render static file for pathname "/docs/intro"
|
|
28
|
+
[cause]: RangeError: Maximum call stack size exceeded
|
|
29
|
+
at RegExp.exec (<anonymous>)
|
|
30
|
+
at F (server.bundle.js:15836:87)
|
|
31
|
+
at Ka (server.bundle.js:15845:249)
|
|
32
|
+
at Pa (server.bundle.js:15853:68)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Fix: switched both wrappers to import from `@theme-init/X`, which always resolves to the un-wrapped initial component from the theme chain.
|
|
36
|
+
|
|
37
|
+
- **`getThemePath()` returning `undefined` crashes core.** With `askAi.enabled: false` (or `askAi.placement: 'none'`), v0.1.0's `getThemePath` returned `undefined`, which `@docusaurus/core` then handed to `path.join` inside `webpack/server.js`. Fix: the plugin now constructs its plugin object conditionally and only attaches `getThemePath` when the theme is enabled. This is the idiomatic signal that a plugin contributes no theme.
|
|
38
|
+
|
|
39
|
+
### Notes for consumers
|
|
40
|
+
|
|
41
|
+
No config changes required. Bump the version and rebuild.
|
|
42
|
+
|
|
3
43
|
## 0.1.0
|
|
4
44
|
|
|
5
45
|
Initial release.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackql/docusaurus-plugin-aeo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "AEO (Answer Engine Optimization) helpers for Docusaurus: .md companion files, llms.txt / llms-full.txt, an Ask AI dropdown, and /ai/* route conventions.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|
package/src/index.js
CHANGED
|
@@ -113,23 +113,33 @@ module.exports = function pluginAeo(context, rawOptions) {
|
|
|
113
113
|
// Map<pluginName, { plugin: { name, id }, content: any }>
|
|
114
114
|
const loadedContentByPlugin = new Map();
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
const themeEnabled =
|
|
117
|
+
options.askAi.enabled && options.askAi.placement !== 'none';
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
return path.resolve(__dirname, './theme');
|
|
124
|
-
},
|
|
119
|
+
const plugin = {
|
|
120
|
+
name: '@stackql/docusaurus-plugin-aeo',
|
|
125
121
|
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
// Surface the askAi config to theme components. setGlobalData MUST be
|
|
123
|
+
// called from contentLoaded - Docusaurus does not accept it from
|
|
124
|
+
// allContentLoaded, and theme components read it via
|
|
125
|
+
// usePluginData('@stackql/docusaurus-plugin-aeo') at render time.
|
|
126
|
+
async contentLoaded({ actions }) {
|
|
127
|
+
await actions.setGlobalData({
|
|
128
|
+
askAi: {
|
|
129
|
+
enabled: options.askAi.enabled,
|
|
130
|
+
providerOrder: options.askAi.providerOrder,
|
|
131
|
+
promptTemplate: options.askAi.promptTemplate,
|
|
132
|
+
placement: options.askAi.placement,
|
|
133
|
+
companionsEnabled: options.companions.enabled,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
128
136
|
},
|
|
129
137
|
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
|
|
138
|
+
// Cross-plugin loaded content (docs, blog, pages) is only delivered to
|
|
139
|
+
// allContentLoaded in Docusaurus 3.x. contentLoaded receives only the
|
|
140
|
+
// current plugin's own content, so feature 1 needs this hook to see
|
|
141
|
+
// the docs/blog source files it has to mirror.
|
|
142
|
+
async allContentLoaded({ allContent }) {
|
|
133
143
|
if (allContent) {
|
|
134
144
|
for (const [pluginName, byId] of Object.entries(allContent)) {
|
|
135
145
|
if (!byId) continue;
|
|
@@ -143,16 +153,6 @@ module.exports = function pluginAeo(context, rawOptions) {
|
|
|
143
153
|
}
|
|
144
154
|
}
|
|
145
155
|
|
|
146
|
-
await actions.setGlobalData({
|
|
147
|
-
askAi: {
|
|
148
|
-
enabled: options.askAi.enabled,
|
|
149
|
-
providerOrder: options.askAi.providerOrder,
|
|
150
|
-
promptTemplate: options.askAi.promptTemplate,
|
|
151
|
-
placement: options.askAi.placement,
|
|
152
|
-
companionsEnabled: options.companions.enabled,
|
|
153
|
-
},
|
|
154
|
-
});
|
|
155
|
-
|
|
156
156
|
if (options.aiRoutes.validate) {
|
|
157
157
|
validateAiRoutes({
|
|
158
158
|
loadedContentByPlugin,
|
|
@@ -188,13 +188,18 @@ module.exports = function pluginAeo(context, rawOptions) {
|
|
|
188
188
|
}
|
|
189
189
|
},
|
|
190
190
|
};
|
|
191
|
-
};
|
|
192
191
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
192
|
+
// Only contribute a theme path when the Ask AI button is enabled.
|
|
193
|
+
// Returning undefined or an invalid value from getThemePath crashes
|
|
194
|
+
// @docusaurus/core in webpack/server.js; omitting the method entirely
|
|
195
|
+
// is the idiomatic signal that this plugin contributes no theme.
|
|
196
|
+
if (themeEnabled) {
|
|
197
|
+
plugin.getThemePath = () => path.resolve(__dirname, './theme');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return plugin;
|
|
200
201
|
};
|
|
202
|
+
|
|
203
|
+
// Intentionally no validateOptions export: Docusaurus applies its own default
|
|
204
|
+
// option normalization (including id: 'default') when this is absent.
|
|
205
|
+
// The plugin's handwritten validator runs at construction time.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
// See note in src/theme/DocItem/Footer/index.jsx - @theme-init avoids the
|
|
3
|
+
// SSR recursion that @theme-original triggers when a plugin (not a theme)
|
|
4
|
+
// contributes the wrapper.
|
|
5
|
+
import Footer from '@theme-init/BlogPostItem/Footer';
|
|
3
6
|
import AskAiButton from '@theme/AskAiButton';
|
|
4
7
|
|
|
5
8
|
export default function FooterWrapper(props) {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
// Use @theme-init (the initial component from the theme chain, before any
|
|
3
|
+
// wrappers) instead of @theme-original. When a plugin contributes both the
|
|
4
|
+
// wrapper and is the only contributor in the wrapper layer,
|
|
5
|
+
// @theme-original/X resolves back to this wrapper file and renders infinitely
|
|
6
|
+
// on every SSR pass. @theme-init/X always resolves to the un-wrapped origin.
|
|
7
|
+
import Footer from '@theme-init/DocItem/Footer';
|
|
3
8
|
import AskAiButton from '@theme/AskAiButton';
|
|
4
9
|
|
|
5
10
|
export default function FooterWrapper(props) {
|