docusaurus-plugin-myog 0.0.0-stage → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MyOG.social
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,126 @@
1
- # Temporary Holding Version
1
+ # docusaurus-plugin-myog
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Adds an `og:image` and `twitter:image` to each page of a Docusaurus site. It
4
+ covers docs, versioned docs, blog posts, blog lists, tag pages and custom pages.
5
+ [MyOG.social](https://myog.social) generates each image from the page's URL,
6
+ using its content and branding automatically.
7
+
8
+ The plugin writes metadata. It does not fetch images or call MyOG during builds
9
+ or navigation. Social platforms request the image URL when they preview a page.
10
+
11
+ Maintained by [MyOG.social](https://myog.social). The plugin is MIT licensed;
12
+ the image service requires a paid plan or card trial.
13
+
14
+ ## Requirements and tested support
15
+
16
+ - Docusaurus **3.10 or a later 3.x release**: stable `@docusaurus/core`
17
+ `>=3.10.0 <4.0.0`. Tested with 3.10.0 and 3.10.2. The runtime check rejects
18
+ older releases, prereleases and Docusaurus 4, which is not supported yet.
19
+ - Docusaurus 3.10.0 itself fails plain webpack builds with webpack 5.106 or
20
+ newer, before any plugin runs (its webpackbar 6 dependency). 3.10.1 fixed
21
+ this. Use 3.10.1 or later, or Rspack.
22
+ - Tested with the classic preset, React/react-dom **19.3.1** and **18.3.1**,
23
+ Node.js **24.1.0** and **20.11.0**, plain **webpack** and **Rspack** builds.
24
+ Rspack uses `@docusaurus/faster` at the core version with `future.v4: true`;
25
+ the webpack fixture explicitly disables `future.v4` and `future.faster`.
26
+ These are representative combinations, not every combination of those values.
27
+ - Node.js 20.11.0 or newer is required. Later Node releases are not all tested.
28
+ - A MyOG account with the site's domain registered and an active subscription
29
+ or trial. Image generation and refreshes use the plan's credits. The plugin
30
+ does not register domains, start trials or bypass usage limits.
31
+ - Your account ID, copied from the `a=` value in the install code on
32
+ [the Docusaurus guide](https://myog.social/docs/docusaurus). It appears in each
33
+ image URL and is not a secret.
34
+
35
+ The integration uses the documented `configureWebpack` hook, but remapping the
36
+ resolved theme alias and relying on core App's metadata render order are
37
+ **undocumented internal contracts**. The build fails if the required alias is
38
+ missing, unsafe to wrap, or the core version is unsupported. See
39
+ [DEVELOPMENT.md](https://github.com/MyOG-social/docusaurus-plugin-myog/blob/main/DEVELOPMENT.md)
40
+ for the verification recipe.
41
+
42
+ ## Install
43
+
44
+ In your Docusaurus 3.10+ site:
45
+
46
+ ```sh
47
+ pnpm add docusaurus-plugin-myog@^0.2.0
48
+ ```
49
+
50
+ Add the plugin to `docusaurus.config.ts` (or `.js`):
51
+
52
+ ```ts
53
+ export default {
54
+ url: "https://docs.example.com",
55
+ // ...
56
+ plugins: [["docusaurus-plugin-myog", { account: "<account ID>" }]],
57
+ }
58
+ ```
59
+
60
+ Keep `themeConfig.image` and `themeConfig.metadata` if you use them. MyOG replaces
61
+ their global `og:image` and `twitter:image` values; their other metadata stays.
62
+ Remove static `og:image` and `twitter:image` entries from `headTags`, because
63
+ Helmet cannot deduplicate them. The plugin warns about that conflict.
64
+
65
+ Run `pnpm run build`, then inspect the generated HTML's `<head>`. Each image
66
+ should appear once. On pages without an override, the MyOG URL's `url` parameter
67
+ should decode to that page's `og:url`.
68
+
69
+ ## Options
70
+
71
+ | Option | Required | Default | Description |
72
+ | ---------- | -------- | ------------------ | --------------------------------------------------------------------------------------------- |
73
+ | `account` | yes | | Your MyOG account ID (letters and digits). |
74
+ | `template` | no | `screenshot-right` | `brand`, `screenshot`, `screenshot-right`, `screenshot-bottom` or `screenshot-bottom-framed`. |
75
+
76
+ The template selects the image composition. Automatic URL generation remains
77
+ the product workflow; there is no per-page template setup.
78
+
79
+ ## Metadata priority and custom components
80
+
81
+ From lowest to highest priority:
82
+
83
+ 1. Site defaults, including `themeConfig.image` and `themeConfig.metadata`.
84
+ 2. The MyOG image for the current page.
85
+ 3. A page's front matter `image` or MDX `<head>` image tags.
86
+
87
+ An MDX block that sets only `og:image` keeps MyOG's `twitter:image`. Set both
88
+ when both should use the override. A front matter image can equal the old
89
+ global image and still wins.
90
+
91
+ The plugin does not provide `@theme/Root`. Existing replacing or wrapping site
92
+ Roots and Roots from other plugins retain their normal Docusaurus behavior.
93
+ No Root wrapping or plugin ordering change is required for MyOG.
94
+
95
+ A site-ejected `SiteMetadata` is preserved: MyOG wraps the component already
96
+ resolved by Docusaurus and passes its props through. A theme's component or
97
+ core's empty fallback is handled the same way. The plugin does not substitute
98
+ `@theme-init/SiteMetadata`, which could discard theme or site metadata. A custom
99
+ component must retain any first-party metadata the site needs. Competing plugins
100
+ that remap this same alias are outside tested support.
101
+
102
+ ## Limits
103
+
104
+ - `url`, `baseUrl` and `trailingSlash` must match the deployed site. The image
105
+ URL uses `siteConfig.url` plus the current route path, matching Docusaurus's
106
+ `og:url` in generated HTML.
107
+ - During client navigation, non-ASCII paths use the browser's percent-encoded
108
+ pathname. Generated HTML retains Docusaurus's canonical URL representation.
109
+ - Each distinct page URL gets its own image; `/a` and `/a/` count separately.
110
+ Generation and periodic refreshes use credits from the monthly allowance
111
+ ([pricing](https://myog.social/pricing)). A large site can exhaust it.
112
+ - One plugin instance per site. Static `headTags` image tags must be removed.
113
+ - Verified with `docusaurus build`, `serve` hydration/client navigation and a
114
+ `docusaurus start` smoke test. Only the `en` locale was tested. i18n locale
115
+ paths and other themes are not verified. The experimental hash router is
116
+ unsupported because it produces one HTML page.
117
+ - Social platforms cache images; use their debugger to refresh a card.
118
+
119
+ ## Uninstall
120
+
121
+ Remove the plugin entry and run `pnpm remove docusaurus-plugin-myog`.
122
+ Docusaurus uses the existing theme image settings again.
123
+
124
+ ## License
125
+
126
+ MIT
@@ -0,0 +1,7 @@
1
+ export const MYOG_IMAGE_ENDPOINT = "https://api.myog.social/og";
2
+ // Matches the install snippet on https://myog.social/docs/docusaurus: the page URL is the
3
+ // deployed site URL plus the route path, the same value Docusaurus emits as og:url.
4
+ export function buildImageURL({ account, template, siteURL, pathname, }) {
5
+ const pageURL = siteURL + pathname;
6
+ return `${MYOG_IMAGE_ENDPOINT}?a=${encodeURIComponent(account)}&url=${encodeURIComponent(pageURL)}&template=${encodeURIComponent(template)}&src=docs`;
7
+ }
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import Head from "@docusaurus/Head";
3
+ import { useLocation } from "@docusaurus/router";
4
+ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
5
+ import { usePluginData } from "@docusaurus/useGlobalData";
6
+ import OriginalSiteMetadata from "@myog-original/SiteMetadata";
7
+ import { buildImageURL } from "../client/imageURL.js";
8
+ export default function SiteMetadata(props) {
9
+ const { siteConfig } = useDocusaurusContext();
10
+ const { pathname } = useLocation();
11
+ const { account, template } = usePluginData("docusaurus-plugin-myog");
12
+ const imageURL = buildImageURL({ account, template, siteURL: siteConfig.url, pathname });
13
+ return (_jsxs(_Fragment, { children: [_jsx(OriginalSiteMetadata, { ...props }), _jsxs(Head, { children: [_jsx("meta", { property: "og:image", content: imageURL }), _jsx("meta", { name: "twitter:image", content: imageURL })] })] }));
14
+ }
@@ -0,0 +1,3 @@
1
+ export declare function findConflicts({ headTags }: {
2
+ headTags?: unknown;
3
+ }): string[];
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findConflicts = findConflicts;
4
+ const IMAGE_META_KEYS = new Set(["og:image", "twitter:image"]);
5
+ function findConflicts({ headTags }) {
6
+ const staticTags = Array.isArray(headTags) ? headTags : [];
7
+ return staticTags.some((tag) => IMAGE_META_KEYS.has(String(tag?.attributes?.property)) ||
8
+ IMAGE_META_KEYS.has(String(tag?.attributes?.name)))
9
+ ? [
10
+ "headTags contains og:image or twitter:image outside Helmet, which puts a second image tag on every page. Remove those entries.",
11
+ ]
12
+ : [];
13
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { LoadContext, Plugin } from "@docusaurus/types";
2
+ import { type PluginOptions } from "./options";
3
+ export type { PluginOptions, Template } from "./options";
4
+ export default function pluginMyOG(context: LoadContext, options: PluginOptions): Plugin<void>;
package/lib/index.js ADDED
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = pluginMyOG;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const node_fs_1 = require("node:fs");
9
+ const conflicts_1 = require("./conflicts");
10
+ const siteMetadata_1 = require("./siteMetadata");
11
+ const options_1 = require("./options");
12
+ function pluginMyOG(context, options) {
13
+ const coreManifest = require.resolve("@docusaurus/core/package.json", {
14
+ paths: [context.siteDir],
15
+ });
16
+ const { version } = JSON.parse((0, node_fs_1.readFileSync)(coreManifest, "utf8"));
17
+ (0, siteMetadata_1.assertSupportedVersion)(version);
18
+ const data = (0, options_1.normalizeOptions)(options);
19
+ for (const conflict of (0, conflicts_1.findConflicts)({
20
+ headTags: context.siteConfig.headTags,
21
+ })) {
22
+ console.warn(`[${options_1.PLUGIN_NAME}] ${conflict}`);
23
+ }
24
+ return {
25
+ name: options_1.PLUGIN_NAME,
26
+ contentLoaded({ actions }) {
27
+ actions.setGlobalData(data);
28
+ },
29
+ configureWebpack(config) {
30
+ return (0, siteMetadata_1.wrapSiteMetadata)({
31
+ aliases: config.resolve?.alias,
32
+ wrapper: node_path_1.default.join(__dirname, "components/SiteMetadata.js"),
33
+ });
34
+ },
35
+ };
36
+ }
@@ -0,0 +1,15 @@
1
+ export declare const PLUGIN_NAME = "docusaurus-plugin-myog";
2
+ export declare const TEMPLATES: readonly ["brand", "screenshot", "screenshot-right", "screenshot-bottom", "screenshot-bottom-framed"];
3
+ export type Template = (typeof TEMPLATES)[number];
4
+ export declare const DEFAULT_TEMPLATE: Template;
5
+ export type PluginOptions = {
6
+ /** Your MyOG account ID, the `a=` value in the install snippet on https://myog.social/docs/docusaurus. */
7
+ account: string;
8
+ /** Image layout. Defaults to `screenshot-right`. */
9
+ template?: Template;
10
+ };
11
+ export type MyOGGlobalData = {
12
+ account: string;
13
+ template: Template;
14
+ };
15
+ export declare function normalizeOptions(raw: unknown): MyOGGlobalData;
package/lib/options.js ADDED
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_TEMPLATE = exports.TEMPLATES = exports.PLUGIN_NAME = void 0;
4
+ exports.normalizeOptions = normalizeOptions;
5
+ exports.PLUGIN_NAME = "docusaurus-plugin-myog";
6
+ exports.TEMPLATES = [
7
+ "brand",
8
+ "screenshot",
9
+ "screenshot-right",
10
+ "screenshot-bottom",
11
+ "screenshot-bottom-framed",
12
+ ];
13
+ exports.DEFAULT_TEMPLATE = "screenshot-right";
14
+ function fail(message) {
15
+ throw new Error(`[${exports.PLUGIN_NAME}] ${message}`);
16
+ }
17
+ function normalizeOptions(raw) {
18
+ const options = (raw ?? {});
19
+ const { id, account, template, ...unknown } = options;
20
+ if (id !== undefined && id !== "default") {
21
+ fail("supports a single instance; remove the `id` option.");
22
+ }
23
+ const unknownKeys = Object.keys(unknown);
24
+ if (unknownKeys.length > 0) {
25
+ fail(`unknown option(s): ${unknownKeys.join(", ")}. Supported: account, template.`);
26
+ }
27
+ const trimmedAccount = typeof account === "string" ? account.trim() : "";
28
+ if (!/^[A-Za-z0-9]{1,32}$/.test(trimmedAccount)) {
29
+ fail("`account` must be your MyOG account ID (letters and digits), copied from the install snippet at https://myog.social/docs/docusaurus.");
30
+ }
31
+ const chosenTemplate = template ?? exports.DEFAULT_TEMPLATE;
32
+ if (!exports.TEMPLATES.includes(chosenTemplate)) {
33
+ fail(`\`template\` must be one of: ${exports.TEMPLATES.join(", ")}.`);
34
+ }
35
+ return { account: trimmedAccount, template: chosenTemplate };
36
+ }
@@ -0,0 +1,13 @@
1
+ export declare const SUPPORTED_CORE_RANGE = ">=3.10.0 <4.0.0";
2
+ export declare function assertSupportedVersion(version: string): void;
3
+ export declare function wrapSiteMetadata({ aliases, wrapper }: {
4
+ aliases: unknown;
5
+ wrapper: string;
6
+ }): {
7
+ resolve: {
8
+ alias: {
9
+ "@theme/SiteMetadata": string;
10
+ "@myog-original/SiteMetadata": string;
11
+ };
12
+ };
13
+ };
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SUPPORTED_CORE_RANGE = void 0;
4
+ exports.assertSupportedVersion = assertSupportedVersion;
5
+ exports.wrapSiteMetadata = wrapSiteMetadata;
6
+ const node_path_1 = require("node:path");
7
+ const options_1 = require("./options");
8
+ exports.SUPPORTED_CORE_RANGE = ">=3.10.0 <4.0.0";
9
+ function assertSupportedVersion(version) {
10
+ const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
11
+ if (!match || Number(match[1]) !== 3 || Number(match[2]) < 10) {
12
+ throw new Error(`[${options_1.PLUGIN_NAME}] Docusaurus ${version} is not supported. Use a stable @docusaurus/core ${exports.SUPPORTED_CORE_RANGE} release or remove this plugin and use the manual guide at https://myog.social/docs/docusaurus. The SiteMetadata integration requires the tested theme alias and App render-order contracts.`);
13
+ }
14
+ }
15
+ function wrapSiteMetadata({ aliases, wrapper }) {
16
+ const resolved = aliases;
17
+ const original = resolved?.["@theme/SiteMetadata"];
18
+ if (!resolved ||
19
+ Array.isArray(resolved) ||
20
+ typeof original !== "string" ||
21
+ !(0, node_path_1.isAbsolute)(original) ||
22
+ original === wrapper ||
23
+ resolved["@myog-original/SiteMetadata"] !== undefined) {
24
+ throw new Error(`[${options_1.PLUGIN_NAME}] Cannot wrap the resolved @theme/SiteMetadata alias safely. Use a supported Docusaurus release (>=3.10.0 <4.0.0) and remove competing SiteMetadata alias remaps, or remove this plugin and use https://myog.social/docs/docusaurus. No fallback is substituted because it could discard your site's metadata.`);
25
+ }
26
+ return {
27
+ resolve: {
28
+ alias: {
29
+ "@theme/SiteMetadata": wrapper,
30
+ "@myog-original/SiteMetadata": original,
31
+ },
32
+ },
33
+ };
34
+ }
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "docusaurus-plugin-myog",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "Adds a MyOG.social og:image and twitter:image to every Docusaurus page, generated from each page's URL",
5
+ "keywords": [
6
+ "docusaurus",
7
+ "docusaurus-plugin",
8
+ "open-graph",
9
+ "og-image",
10
+ "twitter-card",
11
+ "social-card"
12
+ ],
13
+ "homepage": "https://myog.social/docs/docusaurus",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/MyOG-social/docusaurus-plugin-myog.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/MyOG-social/docusaurus-plugin-myog/issues"
20
+ },
21
+ "license": "MIT",
22
+ "main": "lib/index.js",
23
+ "types": "lib/index.d.ts",
24
+ "files": [
25
+ "lib"
26
+ ],
27
+ "engines": {
28
+ "node": ">=20.11.0"
29
+ },
30
+ "peerDependencies": {
31
+ "@docusaurus/core": ">=3.10.0 <4.0.0",
32
+ "@docusaurus/types": ">=3.10.0 <4.0.0",
33
+ "react": "^18.0.0 || ^19.0.0"
34
+ },
35
+ "peerDependenciesMeta": {
36
+ "@docusaurus/types": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "publishConfig": {
41
+ "access": "public",
42
+ "registry": "https://registry.npmjs.org/"
43
+ },
44
+ "devDependencies": {
45
+ "@biomejs/biome": "2.4.14",
46
+ "@docusaurus/module-type-aliases": "3.10.2",
47
+ "@docusaurus/types": "3.10.2",
48
+ "@types/bun": "1.2.16",
49
+ "@types/node": "24.0.0",
50
+ "@types/react": "19.3.0",
51
+ "prettier": "3.7.4",
52
+ "react": "19.3.1",
53
+ "typescript": "5.9.3"
54
+ },
55
+ "scripts": {
56
+ "build": "tsc -p tsconfig.server.json && tsc -p tsconfig.client.json",
57
+ "type-check": "tsc --noEmit",
58
+ "test": "bun run build && bun test tests",
59
+ "lint": "biome lint src tests",
60
+ "format": "prettier --write"
61
+ }
6
62
  }