@ecopages/mdx 0.2.0-alpha.3 → 0.2.0-alpha.31
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 +14 -11
- package/README.md +55 -15
- package/package.json +2 -2
- package/src/mdx-loader-plugin.d.ts +1 -1
- package/src/mdx-loader-plugin.js +12 -4
- package/src/mdx-renderer.d.ts +14 -34
- package/src/mdx-renderer.js +40 -56
- package/src/mdx.constants.d.ts +1 -0
- package/src/mdx.constants.js +4 -0
- package/src/mdx.plugin.d.ts +19 -7
- package/src/mdx.plugin.js +58 -8
- package/src/mdx.types.d.ts +26 -0
- package/src/mdx.types.js +0 -0
- package/src/mdx-loader-plugin.ts +0 -40
- package/src/mdx-renderer.ts +0 -221
- package/src/mdx.plugin.ts +0 -85
package/CHANGELOG.md
CHANGED
|
@@ -8,24 +8,27 @@ All notable changes to `@ecopages/mdx` are documented here.
|
|
|
8
8
|
|
|
9
9
|
### Features
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
- **Async MDX compilation** — The MDX loader plugin now compiles MDX files asynchronously, improving compatibility with async remark/rehype plugins (`9e879dbe`).
|
|
13
|
-
- **Updated type definitions** — Plugin options are more precisely typed to clarify server-rendered MDX route configuration.
|
|
11
|
+
- Added standalone non-React MDX server rendering with async compilation and opt-in `.md` support.
|
|
14
12
|
|
|
15
|
-
###
|
|
13
|
+
### Bug Fixes
|
|
16
14
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- Ambient module declarations cleaned up (`5f46ecc5`).
|
|
20
|
-
- Aligned with full orchestration mode (`fc07bdb0`).
|
|
15
|
+
- Fixed loader registration, Node `source-map` interop, and renderer-owned mixed foreign-subtree rendering for standalone MDX routes.
|
|
16
|
+
- Fixed standalone MDX foreign-subtree payload compatibility coverage and removed the plugin/renderer integration-name import cycle.
|
|
21
17
|
|
|
22
18
|
### Documentation
|
|
23
19
|
|
|
24
|
-
- README
|
|
20
|
+
- Updated the README for standalone non-React MDX usage, `.md` opt-in handling, and compiler configuration.
|
|
21
|
+
|
|
22
|
+
### Tests
|
|
23
|
+
|
|
24
|
+
- Added renderer-level coverage for the foreign-subtree payload compatibility contract.
|
|
25
|
+
|
|
26
|
+
### Refactoring
|
|
27
|
+
|
|
28
|
+
- Replaced the standalone MDX renderer factory with explicit renderer-owned compiler configuration and collected shared MDX plugin and renderer types into a dedicated module.
|
|
25
29
|
|
|
26
30
|
---
|
|
27
31
|
|
|
28
32
|
## Migration Notes
|
|
29
33
|
|
|
30
|
-
-
|
|
31
|
-
- The `useReact` option and React-specific HMR hooks have been removed.
|
|
34
|
+
- Use `reactPlugin({ mdx: { enabled: true } })` for React-backed MDX routes; the standalone `@ecopages/mdx` plugin now targets non-React JSX runtimes.
|
package/README.md
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ecopages/mdx
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Integration plugin for standalone MDX support in Ecopages for non-React JSX runtimes such as `@kitajs/html`. Use it when MDX should render directly on the server without React hydration.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
bun add @ecopages/mdx @kitajs/html @mdx-js/mdx
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
`@kitajs/html` and `@mdx-js/mdx` are required peer dependencies for this package.
|
|
12
|
+
|
|
11
13
|
## Usage
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
Import and apply the `mdxPlugin` in your `eco.config.ts`:
|
|
14
16
|
|
|
15
17
|
```ts
|
|
16
|
-
import { ConfigBuilder } from '@ecopages/core';
|
|
18
|
+
import { ConfigBuilder } from '@ecopages/core/config-builder';
|
|
17
19
|
import { mdxPlugin } from '@ecopages/mdx';
|
|
18
20
|
|
|
19
21
|
const config = await new ConfigBuilder()
|
|
@@ -29,11 +31,55 @@ By default, the standalone plugin uses:
|
|
|
29
31
|
- `jsxImportSource: '@kitajs/html'`
|
|
30
32
|
- `jsxRuntime: 'automatic'`
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
## What This Integration Owns
|
|
35
|
+
|
|
36
|
+
- `.mdx` route files.
|
|
37
|
+
- Optional `.md` routes when you opt them into `extensions`.
|
|
38
|
+
- MDX compilation against a non-React JSX runtime.
|
|
39
|
+
|
|
40
|
+
## Configure Markdown Extensions
|
|
41
|
+
|
|
42
|
+
Use `extensions` when both `.mdx` and `.md` files should run through the MDX loader.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { mdxPlugin } from '@ecopages/mdx';
|
|
46
|
+
|
|
47
|
+
mdxPlugin({
|
|
48
|
+
extensions: ['.mdx', '.md'],
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Compiler Options
|
|
53
|
+
|
|
54
|
+
Pass `compilerOptions` to add remark, rehype, or recma plugins while keeping the non-React JSX runtime managed by the integration.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { mdxPlugin } from '@ecopages/mdx';
|
|
58
|
+
|
|
59
|
+
mdxPlugin({
|
|
60
|
+
compilerOptions: {
|
|
61
|
+
remarkPlugins: [],
|
|
62
|
+
rehypePlugins: [],
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
> [!WARNING]
|
|
68
|
+
> React runtimes are intentionally rejected by this standalone plugin.
|
|
69
|
+
|
|
70
|
+
## Mixed Rendering
|
|
33
71
|
|
|
34
|
-
|
|
72
|
+
Standalone MDX can own the page shell or nested MDX foreign subtrees in a mixed-renderer app. When another integration reaches an MDX-owned foreign child, Ecopages hands that foreign subtree back to the MDX renderer so the MDX runtime can finish serialization before the outer renderer resumes.
|
|
35
73
|
|
|
36
|
-
|
|
74
|
+
Important:
|
|
75
|
+
|
|
76
|
+
- Components that may render foreign children must declare those children in `config.dependencies.components`.
|
|
77
|
+
- Ecopages validates mixed-renderer ownership from declared dependencies during render preparation rather than inferring every foreign subtree from rendered HTML alone.
|
|
78
|
+
- Standalone MDX keeps its own page normalization and non-React JSX runtime behavior.
|
|
79
|
+
|
|
80
|
+
## Using MDX with React
|
|
81
|
+
|
|
82
|
+
If you are using `@ecopages/react` and building a full React application, **do not** use this standalone MDX plugin. Instead, enable MDX directly within the React plugin configuration to ensure unified hydration, client-side routing, and HMR:
|
|
37
83
|
|
|
38
84
|
```ts
|
|
39
85
|
import { reactPlugin } from '@ecopages/react';
|
|
@@ -44,9 +90,3 @@ reactPlugin({
|
|
|
44
90
|
mdx: { enabled: true },
|
|
45
91
|
});
|
|
46
92
|
```
|
|
47
|
-
|
|
48
|
-
See the `@ecopages/react` documentation for details.
|
|
49
|
-
|
|
50
|
-
## React runtimes are not supported here
|
|
51
|
-
|
|
52
|
-
Standalone `mdxPlugin()` rejects `jsxImportSource: 'react'` and related React JSX runtimes. For React-backed MDX, use [@ecopages/react](../react/README.md) with `mdx.enabled`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/mdx",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.31",
|
|
4
4
|
"description": "MDX plugin for Ecopages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"directory": "packages/integrations/mdx"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@ecopages/core": "0.2.0-alpha.
|
|
40
|
+
"@ecopages/core": "0.2.0-alpha.31",
|
|
41
41
|
"@kitajs/html": "^4.1.0",
|
|
42
42
|
"@mdx-js/mdx": "^3.1.0"
|
|
43
43
|
},
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { EcoBuildPlugin } from '@ecopages/core/
|
|
1
|
+
import type { EcoBuildPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
2
2
|
import { type CompileOptions } from '@mdx-js/mdx';
|
|
3
3
|
export declare function createMdxLoaderPlugin(compilerOptions?: CompileOptions): EcoBuildPlugin;
|
package/src/mdx-loader-plugin.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { compile } from "@mdx-js/mdx";
|
|
4
|
-
import
|
|
4
|
+
import sourceMap from "source-map";
|
|
5
5
|
import { VFile } from "vfile";
|
|
6
|
+
function resolveCompileFormat(filePath, compilerOptions) {
|
|
7
|
+
const configuredFormat = compilerOptions?.format;
|
|
8
|
+
if (configuredFormat && configuredFormat !== "detect") {
|
|
9
|
+
return configuredFormat;
|
|
10
|
+
}
|
|
11
|
+
return path.extname(filePath).toLowerCase() === ".md" ? "mdx" : configuredFormat;
|
|
12
|
+
}
|
|
6
13
|
function createMdxLoaderPlugin(compilerOptions) {
|
|
7
14
|
const mdxExtensions = compilerOptions?.mdxExtensions ?? [".mdx"];
|
|
8
15
|
const mdExtensions = compilerOptions?.mdExtensions ?? [".md"];
|
|
@@ -18,13 +25,14 @@ function createMdxLoaderPlugin(compilerOptions) {
|
|
|
18
25
|
const file = new VFile({ path: filePath, value: source });
|
|
19
26
|
const compiled = await compile(file, {
|
|
20
27
|
...compilerOptions,
|
|
21
|
-
|
|
28
|
+
format: resolveCompileFormat(filePath, compilerOptions),
|
|
29
|
+
SourceMapGenerator: sourceMap.SourceMapGenerator
|
|
22
30
|
});
|
|
23
|
-
const
|
|
31
|
+
const inlineSourceMap = compiled.map ? `
|
|
24
32
|
//# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(compiled.map)).toString("base64")}
|
|
25
33
|
` : "";
|
|
26
34
|
return {
|
|
27
|
-
contents: `${String(compiled.value)}${
|
|
35
|
+
contents: `${String(compiled.value)}${inlineSourceMap}`,
|
|
28
36
|
loader: compilerOptions?.jsx ? "jsx" : "js",
|
|
29
37
|
resolveDir: path.dirname(args.path)
|
|
30
38
|
};
|
package/src/mdx-renderer.d.ts
CHANGED
|
@@ -2,50 +2,30 @@
|
|
|
2
2
|
* This module contains the MDX renderer
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
|
-
import type {
|
|
5
|
+
import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoPageFile, EcoPagesElement, IntegrationRendererRenderOptions, RouteRendererBody } from '@ecopages/core';
|
|
6
6
|
import { IntegrationRenderer, type RenderToResponseContext } from '@ecopages/core/route-renderer/integration-renderer';
|
|
7
|
-
import type {
|
|
7
|
+
import type { ProcessedAsset } from '@ecopages/core/services/asset-processing-service';
|
|
8
8
|
import type { CompileOptions } from '@mdx-js/mdx';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*/
|
|
12
|
-
export type MDXFile = {
|
|
13
|
-
default: EcoComponent;
|
|
14
|
-
config?: EcoComponentConfig;
|
|
15
|
-
getMetadata: GetMetadata;
|
|
16
|
-
};
|
|
9
|
+
import type { MDXRendererOptions } from './mdx.types.js';
|
|
10
|
+
export type { MDXFile, MDXRendererConfig, MDXRendererOptions } from './mdx.types.js';
|
|
17
11
|
/**
|
|
18
12
|
* Options for the MDX renderer
|
|
19
13
|
*/
|
|
20
|
-
interface
|
|
14
|
+
interface MDXIntegrationRendererOptions<C = EcoPagesElement> extends IntegrationRendererRenderOptions<C> {
|
|
21
15
|
}
|
|
22
16
|
/**
|
|
23
17
|
* A renderer for the MDX integration.
|
|
24
18
|
*/
|
|
25
19
|
export declare class MDXRenderer extends IntegrationRenderer<EcoPagesElement> {
|
|
26
20
|
name: string;
|
|
27
|
-
compilerOptions: CompileOptions;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
protected importPageFile(file: string): Promise<EcoPageFile<{
|
|
37
|
-
layout?: EcoComponent<any> | {
|
|
38
|
-
config: EcoComponentConfig | undefined;
|
|
39
|
-
};
|
|
40
|
-
}>>;
|
|
41
|
-
render({ params, query, props, locals, pageLocals, metadata, Page, HtmlTemplate, Layout, pageProps, }: MDXIntegrationRendererOpions): Promise<RouteRendererBody>;
|
|
21
|
+
readonly compilerOptions: CompileOptions;
|
|
22
|
+
private isFunctionComponent;
|
|
23
|
+
constructor({ mdxConfig, ...options }: MDXRendererOptions);
|
|
24
|
+
buildPageBrowserGraph(pagePath: string): Promise<{
|
|
25
|
+
assets: ProcessedAsset[];
|
|
26
|
+
}>;
|
|
27
|
+
protected normalizeImportedPageFile<TPageModule extends EcoPageFile>(_file: string, pageModule: TPageModule): TPageModule;
|
|
28
|
+
renderComponent(input: ComponentRenderInput): Promise<ComponentRenderResult>;
|
|
29
|
+
render({ params, query, props, locals, pageLocals, metadata, Page, HtmlTemplate, Layout, pageProps, }: MDXIntegrationRendererOptions): Promise<RouteRendererBody>;
|
|
42
30
|
renderToResponse<P = Record<string, unknown>>(view: EcoComponent<P>, props: P, ctx: RenderToResponseContext): Promise<Response>;
|
|
43
31
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Factory function to create an MDX renderer class with specific compiler options.
|
|
46
|
-
*
|
|
47
|
-
* @param compilerOptions - Compiler options for MDX compilation.
|
|
48
|
-
* @returns A new MDXRenderer class extended with the provided context.
|
|
49
|
-
*/
|
|
50
|
-
export declare function createMDXRenderer(compilerOptions: CompileOptions): typeof MDXRenderer;
|
|
51
|
-
export {};
|
package/src/mdx-renderer.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
+
import { assertIntegrationInvariant } from "@ecopages/core/plugins/integration-plugin";
|
|
1
2
|
import { IntegrationRenderer } from "@ecopages/core/route-renderer/integration-renderer";
|
|
2
|
-
import {
|
|
3
|
-
import { PLUGIN_NAME } from "./mdx.plugin.js";
|
|
3
|
+
import { MDX_PLUGIN_NAME } from "./mdx.constants.js";
|
|
4
4
|
import { rapidhash } from "@ecopages/core/hash";
|
|
5
5
|
class MDXRenderer extends IntegrationRenderer {
|
|
6
|
-
name =
|
|
6
|
+
name = MDX_PLUGIN_NAME;
|
|
7
7
|
compilerOptions;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}) {
|
|
8
|
+
isFunctionComponent(component) {
|
|
9
|
+
return typeof component === "function";
|
|
10
|
+
}
|
|
11
|
+
constructor({ mdxConfig, ...options }) {
|
|
12
12
|
super(options);
|
|
13
|
-
this.compilerOptions = compilerOptions
|
|
13
|
+
this.compilerOptions = mdxConfig?.compilerOptions ?? {};
|
|
14
14
|
}
|
|
15
|
-
async
|
|
15
|
+
async buildPageBrowserGraph(pagePath) {
|
|
16
16
|
const { default: pageComponent } = await this.importPageFile(pagePath);
|
|
17
17
|
const config = pageComponent.config;
|
|
18
18
|
const components = [];
|
|
@@ -32,28 +32,34 @@ class MDXRenderer extends IntegrationRenderer {
|
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
return
|
|
35
|
+
return {
|
|
36
|
+
assets: await this.resolveDependencies(components)
|
|
37
|
+
};
|
|
36
38
|
}
|
|
37
|
-
|
|
39
|
+
normalizeImportedPageFile(_file, pageModule) {
|
|
38
40
|
try {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
config,
|
|
42
|
-
getMetadata
|
|
43
|
-
} = await super.importPageFile(file);
|
|
41
|
+
const mdxModule = pageModule;
|
|
42
|
+
const { default: Page, config, getMetadata } = mdxModule;
|
|
44
43
|
if (typeof Page !== "function") {
|
|
45
44
|
throw new Error("MDX file must export a default function");
|
|
46
45
|
}
|
|
47
46
|
const resolvedLayout = config?.layout;
|
|
48
47
|
if (config) Page.config = config;
|
|
49
48
|
return {
|
|
49
|
+
...pageModule,
|
|
50
50
|
default: Page,
|
|
51
51
|
layout: resolvedLayout,
|
|
52
52
|
getMetadata
|
|
53
53
|
};
|
|
54
54
|
} catch (error) {
|
|
55
|
-
|
|
55
|
+
assertIntegrationInvariant(false, `Error importing MDX file: ${error}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async renderComponent(input) {
|
|
59
|
+
if (!this.isFunctionComponent(input.component)) {
|
|
60
|
+
throw new TypeError("MDX renderer expected a callable component.");
|
|
56
61
|
}
|
|
62
|
+
return this.renderStringComponentWithQueuedForeignSubtrees(input, input.component);
|
|
57
63
|
}
|
|
58
64
|
async render({
|
|
59
65
|
params,
|
|
@@ -68,58 +74,36 @@ class MDXRenderer extends IntegrationRenderer {
|
|
|
68
74
|
pageProps
|
|
69
75
|
}) {
|
|
70
76
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
return await this.renderPageWithDocumentShell({
|
|
78
|
+
page: {
|
|
79
|
+
component: Page,
|
|
80
|
+
props: { params, query, ...props, locals: pageLocals }
|
|
81
|
+
},
|
|
82
|
+
layout: Layout ? {
|
|
83
|
+
component: Layout,
|
|
84
|
+
props: locals ? { locals } : {}
|
|
85
|
+
} : void 0,
|
|
86
|
+
htmlTemplate: HtmlTemplate,
|
|
74
87
|
metadata,
|
|
75
|
-
children,
|
|
76
88
|
pageProps: pageProps || {}
|
|
77
89
|
});
|
|
78
|
-
return this.DOC_TYPE + body;
|
|
79
90
|
} catch (error) {
|
|
80
91
|
throw this.createRenderError("Error rendering page", error);
|
|
81
92
|
}
|
|
82
93
|
}
|
|
83
94
|
async renderToResponse(view, props, ctx) {
|
|
84
95
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} else {
|
|
92
|
-
const children = Layout ? await Layout({ children: pageContent }) : pageContent;
|
|
93
|
-
const HtmlTemplate = await this.getHtmlTemplate();
|
|
94
|
-
const metadata = view.metadata ? await view.metadata({
|
|
95
|
-
params: {},
|
|
96
|
-
query: {},
|
|
97
|
-
props,
|
|
98
|
-
appConfig: this.appConfig
|
|
99
|
-
}) : this.appConfig.defaultMetadata;
|
|
100
|
-
body = this.DOC_TYPE + await HtmlTemplate({
|
|
101
|
-
metadata,
|
|
102
|
-
children,
|
|
103
|
-
pageProps: props
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
return this.createHtmlResponse(body, ctx);
|
|
96
|
+
return await this.renderViewWithDocumentShell({
|
|
97
|
+
view,
|
|
98
|
+
props,
|
|
99
|
+
ctx,
|
|
100
|
+
layout: view.config?.layout
|
|
101
|
+
});
|
|
107
102
|
} catch (error) {
|
|
108
103
|
throw this.createRenderError("Error rendering view", error);
|
|
109
104
|
}
|
|
110
105
|
}
|
|
111
106
|
}
|
|
112
|
-
function createMDXRenderer(compilerOptions) {
|
|
113
|
-
return class extends MDXRenderer {
|
|
114
|
-
constructor(options) {
|
|
115
|
-
super({
|
|
116
|
-
...options,
|
|
117
|
-
compilerOptions
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
107
|
export {
|
|
123
|
-
MDXRenderer
|
|
124
|
-
createMDXRenderer
|
|
108
|
+
MDXRenderer
|
|
125
109
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MDX_PLUGIN_NAME = "MDX";
|
package/src/mdx.plugin.d.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
|
|
2
1
|
import type { EcoPagesElement } from '@ecopages/core';
|
|
3
|
-
import { IntegrationPlugin, type
|
|
4
|
-
import type { CompileOptions } from '@mdx-js/mdx';
|
|
2
|
+
import { IntegrationPlugin, type EcoBuildPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
5
3
|
import { MDXRenderer } from './mdx-renderer.js';
|
|
4
|
+
import type { MDXPluginConfig } from './mdx.types.js';
|
|
5
|
+
export type { MDXPluginConfig, MDXRendererConfig, MDXRendererOptions } from './mdx.types.js';
|
|
6
6
|
/**
|
|
7
7
|
* The name of the MDX plugin
|
|
8
8
|
*/
|
|
9
9
|
export declare const PLUGIN_NAME = "MDX";
|
|
10
|
-
export type MDXPluginConfig = Partial<Omit<IntegrationPluginConfig, 'name'>> & {
|
|
11
|
-
compilerOptions?: CompileOptions;
|
|
12
|
-
};
|
|
13
10
|
/**
|
|
14
11
|
* The MDX plugin class
|
|
15
12
|
* This plugin provides support for MDX components in Ecopages.
|
|
@@ -20,10 +17,25 @@ export type MDXPluginConfig = Partial<Omit<IntegrationPluginConfig, 'name'>> & {
|
|
|
20
17
|
*/
|
|
21
18
|
export declare class MDXPlugin extends IntegrationPlugin<EcoPagesElement> {
|
|
22
19
|
renderer: typeof MDXRenderer;
|
|
23
|
-
private compilerOptions;
|
|
20
|
+
private readonly compilerOptions;
|
|
24
21
|
private mdxLoaderPlugin;
|
|
25
22
|
constructor({ compilerOptions, ...options }?: MDXPluginConfig);
|
|
23
|
+
initializeRenderer(options?: {
|
|
24
|
+
rendererModules?: unknown;
|
|
25
|
+
}): MDXRenderer;
|
|
26
26
|
get plugins(): EcoBuildPlugin[];
|
|
27
|
+
/**
|
|
28
|
+
* Materializes the MDX loader once so config-time sealing and runtime setup
|
|
29
|
+
* can share the same loader instance.
|
|
30
|
+
*/
|
|
31
|
+
private ensureLoaderPlugin;
|
|
32
|
+
/**
|
|
33
|
+
* Prepares the MDX loader contribution before config build seals the manifest.
|
|
34
|
+
*/
|
|
35
|
+
prepareBuildContributions(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Runs runtime-only MDX setup after build contributions are already prepared.
|
|
38
|
+
*/
|
|
27
39
|
setup(): Promise<void>;
|
|
28
40
|
}
|
|
29
41
|
/**
|
package/src/mdx.plugin.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
IntegrationPlugin,
|
|
3
|
+
mergeIntegrationOptions
|
|
4
|
+
} from "@ecopages/core/plugins/integration-plugin";
|
|
3
5
|
import { Logger } from "@ecopages/logger";
|
|
4
6
|
import { createMdxLoaderPlugin } from "./mdx-loader-plugin.js";
|
|
5
|
-
import {
|
|
7
|
+
import { MDX_PLUGIN_NAME } from "./mdx.constants.js";
|
|
8
|
+
import { MDXRenderer } from "./mdx-renderer.js";
|
|
6
9
|
const appLogger = new Logger("[MDXPlugin]");
|
|
7
|
-
const PLUGIN_NAME =
|
|
10
|
+
const PLUGIN_NAME = MDX_PLUGIN_NAME;
|
|
8
11
|
const defaultOptions = {
|
|
9
12
|
format: "detect",
|
|
10
13
|
outputFormat: "program",
|
|
@@ -12,8 +15,20 @@ const defaultOptions = {
|
|
|
12
15
|
jsxRuntime: "automatic",
|
|
13
16
|
development: process.env.NODE_ENV === "development"
|
|
14
17
|
};
|
|
18
|
+
function splitMarkdownExtensions(extensions) {
|
|
19
|
+
const mdExtensions = [];
|
|
20
|
+
const mdxExtensions = [];
|
|
21
|
+
for (const extension of extensions) {
|
|
22
|
+
if (extension === ".md") {
|
|
23
|
+
mdExtensions.push(extension);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
mdxExtensions.push(extension);
|
|
27
|
+
}
|
|
28
|
+
return { mdExtensions, mdxExtensions };
|
|
29
|
+
}
|
|
15
30
|
class MDXPlugin extends IntegrationPlugin {
|
|
16
|
-
renderer;
|
|
31
|
+
renderer = MDXRenderer;
|
|
17
32
|
compilerOptions;
|
|
18
33
|
mdxLoaderPlugin;
|
|
19
34
|
constructor({ compilerOptions, ...options } = { extensions: [".mdx"] }) {
|
|
@@ -22,7 +37,15 @@ class MDXPlugin extends IntegrationPlugin {
|
|
|
22
37
|
extensions: [".mdx"],
|
|
23
38
|
...options
|
|
24
39
|
});
|
|
25
|
-
const
|
|
40
|
+
const { mdExtensions, mdxExtensions } = splitMarkdownExtensions(this.extensions);
|
|
41
|
+
const finalCompilerOptions = mergeIntegrationOptions(
|
|
42
|
+
{
|
|
43
|
+
...defaultOptions,
|
|
44
|
+
mdxExtensions,
|
|
45
|
+
mdExtensions
|
|
46
|
+
},
|
|
47
|
+
compilerOptions
|
|
48
|
+
);
|
|
26
49
|
const jsxImportSource = finalCompilerOptions.jsxImportSource;
|
|
27
50
|
if (jsxImportSource === "react" || (jsxImportSource?.startsWith("react/") ?? false)) {
|
|
28
51
|
throw new Error(
|
|
@@ -30,17 +53,44 @@ class MDXPlugin extends IntegrationPlugin {
|
|
|
30
53
|
);
|
|
31
54
|
}
|
|
32
55
|
this.compilerOptions = finalCompilerOptions;
|
|
33
|
-
this.renderer = createMDXRenderer(finalCompilerOptions);
|
|
34
56
|
appLogger.debug(`MDX plugin configured with jsxImportSource: ${jsxImportSource ?? "default"}`);
|
|
35
57
|
}
|
|
58
|
+
initializeRenderer(options) {
|
|
59
|
+
const renderer = new this.renderer({
|
|
60
|
+
...this.createRendererOptions(options),
|
|
61
|
+
mdxConfig: {
|
|
62
|
+
compilerOptions: this.compilerOptions
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return this.attachRendererRuntimeServices(renderer);
|
|
66
|
+
}
|
|
36
67
|
get plugins() {
|
|
37
68
|
if (this.mdxLoaderPlugin) {
|
|
38
69
|
return [this.mdxLoaderPlugin];
|
|
39
70
|
}
|
|
40
71
|
return [];
|
|
41
72
|
}
|
|
42
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Materializes the MDX loader once so config-time sealing and runtime setup
|
|
75
|
+
* can share the same loader instance.
|
|
76
|
+
*/
|
|
77
|
+
ensureLoaderPlugin() {
|
|
78
|
+
if (this.mdxLoaderPlugin) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
43
81
|
this.mdxLoaderPlugin = createMdxLoaderPlugin(this.compilerOptions);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Prepares the MDX loader contribution before config build seals the manifest.
|
|
85
|
+
*/
|
|
86
|
+
async prepareBuildContributions() {
|
|
87
|
+
this.ensureLoaderPlugin();
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Runs runtime-only MDX setup after build contributions are already prepared.
|
|
91
|
+
*/
|
|
92
|
+
async setup() {
|
|
93
|
+
this.ensureLoaderPlugin();
|
|
44
94
|
await super.setup();
|
|
45
95
|
}
|
|
46
96
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EcoComponent, EcoComponentConfig, EcoPagesAppConfig, GetMetadata } from '@ecopages/core';
|
|
2
|
+
import type { IntegrationPluginConfig } from '@ecopages/core/plugins/integration-plugin';
|
|
3
|
+
import type { AssetProcessingService, ProcessedAsset } from '@ecopages/core/services/asset-processing-service';
|
|
4
|
+
import type { CompileOptions } from '@mdx-js/mdx';
|
|
5
|
+
export type MDXPluginConfig = Partial<Omit<IntegrationPluginConfig, 'name'>> & {
|
|
6
|
+
compilerOptions?: CompileOptions;
|
|
7
|
+
};
|
|
8
|
+
export type MDXRendererConfig = {
|
|
9
|
+
compilerOptions?: CompileOptions;
|
|
10
|
+
};
|
|
11
|
+
export type MDXRendererOptions = {
|
|
12
|
+
appConfig: EcoPagesAppConfig;
|
|
13
|
+
assetProcessingService: AssetProcessingService;
|
|
14
|
+
resolvedIntegrationDependencies: ProcessedAsset[];
|
|
15
|
+
rendererModules?: unknown;
|
|
16
|
+
runtimeOrigin: string;
|
|
17
|
+
mdxConfig?: MDXRendererConfig;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A structure representing an MDX file.
|
|
21
|
+
*/
|
|
22
|
+
export type MDXFile = {
|
|
23
|
+
default: EcoComponent;
|
|
24
|
+
config?: EcoComponentConfig;
|
|
25
|
+
getMetadata: GetMetadata;
|
|
26
|
+
};
|
package/src/mdx.types.js
ADDED
|
File without changes
|
package/src/mdx-loader-plugin.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
|
|
4
|
-
import { type CompileOptions, compile } from '@mdx-js/mdx';
|
|
5
|
-
import { SourceMapGenerator } from 'source-map';
|
|
6
|
-
import { VFile } from 'vfile';
|
|
7
|
-
|
|
8
|
-
export function createMdxLoaderPlugin(compilerOptions?: CompileOptions): EcoBuildPlugin {
|
|
9
|
-
const mdxExtensions = compilerOptions?.mdxExtensions ?? ['.mdx'];
|
|
10
|
-
const mdExtensions = compilerOptions?.mdExtensions ?? ['.md'];
|
|
11
|
-
const allExtensions = [...mdxExtensions, ...mdExtensions];
|
|
12
|
-
const escapedExts = allExtensions.map((ext) => ext.replace('.', '\\.'));
|
|
13
|
-
const filter = new RegExp(`(${escapedExts.join('|')})(\\?.*)?$`);
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
name: 'mdx-loader',
|
|
17
|
-
setup(build) {
|
|
18
|
-
build.onLoad({ filter }, async (args) => {
|
|
19
|
-
const filePath = args.path.includes('?') ? args.path.split('?')[0] : args.path;
|
|
20
|
-
const source = readFileSync(filePath, 'utf-8');
|
|
21
|
-
const file = new VFile({ path: filePath, value: source });
|
|
22
|
-
|
|
23
|
-
const compiled = await compile(file, {
|
|
24
|
-
...compilerOptions,
|
|
25
|
-
SourceMapGenerator,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const sourceMap = compiled.map
|
|
29
|
-
? `\n//# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(compiled.map)).toString('base64')}\n`
|
|
30
|
-
: '';
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
contents: `${String(compiled.value)}${sourceMap}`,
|
|
34
|
-
loader: compilerOptions?.jsx ? 'jsx' : 'js',
|
|
35
|
-
resolveDir: path.dirname(args.path),
|
|
36
|
-
};
|
|
37
|
-
});
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
package/src/mdx-renderer.ts
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This module contains the MDX renderer
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
EcoComponent,
|
|
8
|
-
EcoComponentConfig,
|
|
9
|
-
EcoPageFile,
|
|
10
|
-
EcoPagesElement,
|
|
11
|
-
GetMetadata,
|
|
12
|
-
IntegrationRendererRenderOptions,
|
|
13
|
-
PageMetadataProps,
|
|
14
|
-
RouteRendererBody,
|
|
15
|
-
} from '@ecopages/core';
|
|
16
|
-
import { IntegrationRenderer, type RenderToResponseContext } from '@ecopages/core/route-renderer/integration-renderer';
|
|
17
|
-
import { invariant } from '@ecopages/core/utils/invariant';
|
|
18
|
-
import type { AssetProcessingService, ProcessedAsset } from '@ecopages/core/services/asset-processing-service';
|
|
19
|
-
import type { CompileOptions } from '@mdx-js/mdx';
|
|
20
|
-
import { PLUGIN_NAME } from './mdx.plugin.ts';
|
|
21
|
-
import { rapidhash } from '@ecopages/core/hash';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* A structure representing an MDX file
|
|
25
|
-
*/
|
|
26
|
-
export type MDXFile = {
|
|
27
|
-
default: EcoComponent;
|
|
28
|
-
config?: EcoComponentConfig;
|
|
29
|
-
getMetadata: GetMetadata;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Options for the MDX renderer
|
|
34
|
-
*/
|
|
35
|
-
interface MDXIntegrationRendererOpions<C = EcoPagesElement> extends IntegrationRendererRenderOptions<C> {}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* A renderer for the MDX integration.
|
|
39
|
-
*/
|
|
40
|
-
export class MDXRenderer extends IntegrationRenderer<EcoPagesElement> {
|
|
41
|
-
name = PLUGIN_NAME;
|
|
42
|
-
compilerOptions: CompileOptions;
|
|
43
|
-
|
|
44
|
-
constructor({
|
|
45
|
-
compilerOptions,
|
|
46
|
-
...options
|
|
47
|
-
}: {
|
|
48
|
-
appConfig: any;
|
|
49
|
-
assetProcessingService: AssetProcessingService;
|
|
50
|
-
resolvedIntegrationDependencies: ProcessedAsset[];
|
|
51
|
-
runtimeOrigin: string;
|
|
52
|
-
compilerOptions?: CompileOptions;
|
|
53
|
-
}) {
|
|
54
|
-
super(options);
|
|
55
|
-
this.compilerOptions = compilerOptions || {};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
override async buildRouteRenderAssets(pagePath: string): Promise<ProcessedAsset[]> {
|
|
59
|
-
const { default: pageComponent } = await this.importPageFile(pagePath);
|
|
60
|
-
const config = pageComponent.config;
|
|
61
|
-
const components: Partial<EcoComponent>[] = [];
|
|
62
|
-
|
|
63
|
-
const resolvedLayout = config?.layout;
|
|
64
|
-
|
|
65
|
-
if (resolvedLayout?.config?.dependencies) {
|
|
66
|
-
components.push({ config: resolvedLayout.config });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (config?.dependencies) {
|
|
70
|
-
components.push({
|
|
71
|
-
config: {
|
|
72
|
-
...config,
|
|
73
|
-
__eco: {
|
|
74
|
-
id: rapidhash(pagePath).toString(36),
|
|
75
|
-
file: pagePath,
|
|
76
|
-
integration: this.name,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return await this.resolveDependencies(components);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
protected override async importPageFile(file: string): Promise<
|
|
86
|
-
EcoPageFile<{
|
|
87
|
-
layout?:
|
|
88
|
-
| EcoComponent<any>
|
|
89
|
-
| {
|
|
90
|
-
config: EcoComponentConfig | undefined;
|
|
91
|
-
};
|
|
92
|
-
}>
|
|
93
|
-
> {
|
|
94
|
-
try {
|
|
95
|
-
const {
|
|
96
|
-
default: Page,
|
|
97
|
-
config,
|
|
98
|
-
getMetadata,
|
|
99
|
-
} = (await super.importPageFile(file)) as EcoPageFile<{
|
|
100
|
-
layout?:
|
|
101
|
-
| EcoComponent<any>
|
|
102
|
-
| {
|
|
103
|
-
config: EcoComponentConfig | undefined;
|
|
104
|
-
};
|
|
105
|
-
}> & {
|
|
106
|
-
config?: EcoComponentConfig;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
if (typeof Page !== 'function') {
|
|
110
|
-
throw new Error('MDX file must export a default function');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const resolvedLayout = config?.layout;
|
|
114
|
-
|
|
115
|
-
if (config) Page.config = config;
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
default: Page,
|
|
119
|
-
layout: resolvedLayout,
|
|
120
|
-
getMetadata,
|
|
121
|
-
};
|
|
122
|
-
} catch (error) {
|
|
123
|
-
invariant(false, `Error importing MDX file: ${error}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async render({
|
|
128
|
-
params,
|
|
129
|
-
query,
|
|
130
|
-
props,
|
|
131
|
-
locals,
|
|
132
|
-
pageLocals,
|
|
133
|
-
metadata,
|
|
134
|
-
Page,
|
|
135
|
-
HtmlTemplate,
|
|
136
|
-
Layout,
|
|
137
|
-
pageProps,
|
|
138
|
-
}: MDXIntegrationRendererOpions): Promise<RouteRendererBody> {
|
|
139
|
-
try {
|
|
140
|
-
const pageContent = await Page({ params, query, ...props, locals: pageLocals });
|
|
141
|
-
const children =
|
|
142
|
-
typeof Layout === 'function' ? await Layout({ children: pageContent, locals }) : pageContent;
|
|
143
|
-
|
|
144
|
-
const body = await HtmlTemplate({
|
|
145
|
-
metadata,
|
|
146
|
-
children,
|
|
147
|
-
pageProps: pageProps || {},
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
return this.DOC_TYPE + body;
|
|
151
|
-
} catch (error) {
|
|
152
|
-
throw this.createRenderError('Error rendering page', error);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
async renderToResponse<P = Record<string, unknown>>(
|
|
157
|
-
view: EcoComponent<P>,
|
|
158
|
-
props: P,
|
|
159
|
-
ctx: RenderToResponseContext,
|
|
160
|
-
): Promise<Response> {
|
|
161
|
-
try {
|
|
162
|
-
const Layout = view.config?.layout as
|
|
163
|
-
| ((props: { children: EcoPagesElement } & Record<string, unknown>) => Promise<EcoPagesElement>)
|
|
164
|
-
| undefined;
|
|
165
|
-
|
|
166
|
-
const viewFn = view as (props: P) => Promise<EcoPagesElement>;
|
|
167
|
-
const pageContent = await viewFn(props);
|
|
168
|
-
|
|
169
|
-
let body: string;
|
|
170
|
-
if (ctx.partial) {
|
|
171
|
-
body = pageContent as string;
|
|
172
|
-
} else {
|
|
173
|
-
const children = Layout ? await Layout({ children: pageContent }) : pageContent;
|
|
174
|
-
|
|
175
|
-
const HtmlTemplate = await this.getHtmlTemplate();
|
|
176
|
-
const metadata: PageMetadataProps = view.metadata
|
|
177
|
-
? await view.metadata({
|
|
178
|
-
params: {},
|
|
179
|
-
query: {},
|
|
180
|
-
props: props as Record<string, unknown>,
|
|
181
|
-
appConfig: this.appConfig,
|
|
182
|
-
})
|
|
183
|
-
: this.appConfig.defaultMetadata;
|
|
184
|
-
|
|
185
|
-
body =
|
|
186
|
-
this.DOC_TYPE +
|
|
187
|
-
(await HtmlTemplate({
|
|
188
|
-
metadata,
|
|
189
|
-
children: children as EcoPagesElement,
|
|
190
|
-
pageProps: props as Record<string, unknown>,
|
|
191
|
-
}));
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return this.createHtmlResponse(body, ctx);
|
|
195
|
-
} catch (error) {
|
|
196
|
-
throw this.createRenderError('Error rendering view', error);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Factory function to create an MDX renderer class with specific compiler options.
|
|
203
|
-
*
|
|
204
|
-
* @param compilerOptions - Compiler options for MDX compilation.
|
|
205
|
-
* @returns A new MDXRenderer class extended with the provided context.
|
|
206
|
-
*/
|
|
207
|
-
export function createMDXRenderer(compilerOptions: CompileOptions): typeof MDXRenderer {
|
|
208
|
-
return class extends MDXRenderer {
|
|
209
|
-
constructor(options: {
|
|
210
|
-
appConfig: any;
|
|
211
|
-
assetProcessingService: AssetProcessingService;
|
|
212
|
-
resolvedIntegrationDependencies: ProcessedAsset[];
|
|
213
|
-
runtimeOrigin: string;
|
|
214
|
-
}) {
|
|
215
|
-
super({
|
|
216
|
-
...options,
|
|
217
|
-
compilerOptions,
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
}
|
package/src/mdx.plugin.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
|
|
2
|
-
import type { EcoPagesElement } from '@ecopages/core';
|
|
3
|
-
import { IntegrationPlugin, type IntegrationPluginConfig } from '@ecopages/core/plugins/integration-plugin';
|
|
4
|
-
import { deepMerge } from '@ecopages/core/utils/deep-merge';
|
|
5
|
-
import { Logger } from '@ecopages/logger';
|
|
6
|
-
import type { CompileOptions } from '@mdx-js/mdx';
|
|
7
|
-
import { createMdxLoaderPlugin } from './mdx-loader-plugin.ts';
|
|
8
|
-
import { createMDXRenderer, MDXRenderer } from './mdx-renderer.ts';
|
|
9
|
-
|
|
10
|
-
const appLogger = new Logger('[MDXPlugin]');
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* The name of the MDX plugin
|
|
14
|
-
*/
|
|
15
|
-
export const PLUGIN_NAME = 'MDX';
|
|
16
|
-
|
|
17
|
-
export type MDXPluginConfig = Partial<Omit<IntegrationPluginConfig, 'name'>> & {
|
|
18
|
-
compilerOptions?: CompileOptions;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const defaultOptions: CompileOptions = {
|
|
22
|
-
format: 'detect',
|
|
23
|
-
outputFormat: 'program',
|
|
24
|
-
jsxImportSource: '@kitajs/html',
|
|
25
|
-
jsxRuntime: 'automatic',
|
|
26
|
-
development: process.env.NODE_ENV === 'development',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The MDX plugin class
|
|
31
|
-
* This plugin provides support for MDX components in Ecopages.
|
|
32
|
-
*
|
|
33
|
-
* Standalone `mdxPlugin()` is intended for non-React JSX runtimes such as
|
|
34
|
-
* `@kitajs/html`. React-backed MDX should be configured through
|
|
35
|
-
* `reactPlugin({ mdx: { enabled: true, compilerOptions: ... } })` instead.
|
|
36
|
-
*/
|
|
37
|
-
export class MDXPlugin extends IntegrationPlugin<EcoPagesElement> {
|
|
38
|
-
renderer: typeof MDXRenderer;
|
|
39
|
-
private compilerOptions: CompileOptions;
|
|
40
|
-
private mdxLoaderPlugin: EcoBuildPlugin | undefined;
|
|
41
|
-
|
|
42
|
-
constructor({ compilerOptions, ...options }: MDXPluginConfig = { extensions: ['.mdx'] }) {
|
|
43
|
-
super({
|
|
44
|
-
name: PLUGIN_NAME,
|
|
45
|
-
extensions: ['.mdx'],
|
|
46
|
-
...options,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const finalCompilerOptions = deepMerge({ ...defaultOptions }, compilerOptions);
|
|
50
|
-
const jsxImportSource = finalCompilerOptions.jsxImportSource;
|
|
51
|
-
|
|
52
|
-
if (jsxImportSource === 'react' || (jsxImportSource?.startsWith('react/') ?? false)) {
|
|
53
|
-
throw new Error(
|
|
54
|
-
'Standalone `mdxPlugin()` does not support React JSX runtimes. Use `reactPlugin({ mdx: { enabled: true, compilerOptions: ... } })` instead.',
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
this.compilerOptions = finalCompilerOptions;
|
|
59
|
-
this.renderer = createMDXRenderer(finalCompilerOptions);
|
|
60
|
-
|
|
61
|
-
appLogger.debug(`MDX plugin configured with jsxImportSource: ${jsxImportSource ?? 'default'}`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
override get plugins(): EcoBuildPlugin[] {
|
|
65
|
-
if (this.mdxLoaderPlugin) {
|
|
66
|
-
return [this.mdxLoaderPlugin];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return [];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
override async setup(): Promise<void> {
|
|
73
|
-
this.mdxLoaderPlugin = createMdxLoaderPlugin(this.compilerOptions);
|
|
74
|
-
await super.setup();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Factory function to create an MDX plugin instance.
|
|
80
|
-
* @param options Configuration options for the MDX plugin
|
|
81
|
-
* @returns A new MDXPlugin instance
|
|
82
|
-
*/
|
|
83
|
-
export function mdxPlugin(options?: MDXPluginConfig): MDXPlugin {
|
|
84
|
-
return new MDXPlugin(options);
|
|
85
|
-
}
|