@ecopages/mdx 0.2.0-alpha.5 → 0.2.0-alpha.8
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 +12 -10
- package/README.md +9 -14
- package/package.json +2 -2
- package/src/mdx-loader-plugin.js +12 -4
- package/src/mdx-loader-plugin.ts +27 -4
- package/src/mdx.plugin.d.ts +12 -0
- package/src/mdx.plugin.js +41 -2
- package/src/mdx.plugin.ts +58 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,24 +8,26 @@ All notable changes to `@ecopages/mdx` are documented here.
|
|
|
8
8
|
|
|
9
9
|
### Features
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
11
|
+
- Made the MDX integration standalone so MDX routes can server-render without requiring the React integration.
|
|
12
|
+
- Switched MDX compilation to the async pipeline for async remark and rehype plugin support.
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
- Fixed configured `.md` extensions to compile as MDX instead of plain markdown so top-level `import` and `export` statements work when `.md` is opted in.
|
|
17
|
+
- Fixed loader registration to respect configured extensions so standalone MDX no longer hijacks React `.mdx` pages during shared development and build flows.
|
|
18
|
+
- Fixed native Node startup compatibility by using Node-safe `source-map` interop.
|
|
14
19
|
|
|
15
20
|
### Refactoring
|
|
16
21
|
|
|
17
|
-
- Removed
|
|
18
|
-
- README updated to document standalone MDX usage without React.
|
|
19
|
-
- Ambient module declarations cleaned up (`5f46ecc5`).
|
|
20
|
-
- Aligned with full orchestration mode (`fc07bdb0`).
|
|
22
|
+
- Removed the React-specific renderer and HMR code from the package and aligned MDX with the unified orchestration pipeline.
|
|
21
23
|
|
|
22
24
|
### Documentation
|
|
23
25
|
|
|
24
|
-
-
|
|
26
|
+
- Updated the README for standalone MDX registration and the current integration setup.
|
|
25
27
|
|
|
26
28
|
---
|
|
27
29
|
|
|
28
30
|
## Migration Notes
|
|
29
31
|
|
|
30
|
-
-
|
|
31
|
-
- The `useReact`
|
|
32
|
+
- Register `@ecopages/mdx` and `@ecopages/react` separately when you want MDX server rendering together with React client hydration.
|
|
33
|
+
- The previous React-specific MDX path, including `useReact` and the React-specific HMR hooks, has been removed.
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ecopages/mdx
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Integration plugin for standalone MDX support in Ecopages, specifically designed for non-React JSX runtimes (such as `@kitajs/html`). It configures the MDX compiler to process `.mdx` routes natively.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
bunx jsr add @ecopages/mdx
|
|
@@ -10,10 +10,10 @@ bunx jsr add @ecopages/mdx
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Import and apply the `mdxPlugin` in your `eco.config.ts`:
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import { ConfigBuilder } from '@ecopages/core';
|
|
16
|
+
import { ConfigBuilder } from '@ecopages/core/config-builder';
|
|
17
17
|
import { mdxPlugin } from '@ecopages/mdx';
|
|
18
18
|
|
|
19
19
|
const config = await new ConfigBuilder()
|
|
@@ -29,11 +29,12 @@ By default, the standalone plugin uses:
|
|
|
29
29
|
- `jsxImportSource: '@kitajs/html'`
|
|
30
30
|
- `jsxRuntime: 'automatic'`
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
> [!WARNING]
|
|
33
|
+
> React runtimes are intentionally rejected by this standalone plugin.
|
|
33
34
|
|
|
34
|
-
## Using MDX with React
|
|
35
|
+
## Using MDX with React
|
|
35
36
|
|
|
36
|
-
If you are using `@ecopages/react`
|
|
37
|
+
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
38
|
|
|
38
39
|
```ts
|
|
39
40
|
import { reactPlugin } from '@ecopages/react';
|
|
@@ -44,9 +45,3 @@ reactPlugin({
|
|
|
44
45
|
mdx: { enabled: true },
|
|
45
46
|
});
|
|
46
47
|
```
|
|
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.8",
|
|
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.8",
|
|
41
41
|
"@kitajs/html": "^4.1.0",
|
|
42
42
|
"@mdx-js/mdx": "^3.1.0"
|
|
43
43
|
},
|
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-loader-plugin.ts
CHANGED
|
@@ -2,9 +2,31 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
|
|
4
4
|
import { type CompileOptions, compile } from '@mdx-js/mdx';
|
|
5
|
-
import
|
|
5
|
+
import sourceMap from 'source-map';
|
|
6
6
|
import { VFile } from 'vfile';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the MDX parser mode for a source file.
|
|
10
|
+
*
|
|
11
|
+
* Files with a `.md` extension must be forced into `mdx` mode when the caller
|
|
12
|
+
* explicitly opts them into the MDX pipeline. Leaving the compiler in `detect`
|
|
13
|
+
* mode would treat `.md` files as plain markdown, causing top-level ESM such as
|
|
14
|
+
* `import` and `export` to render as text instead of being compiled.
|
|
15
|
+
*
|
|
16
|
+
* @param filePath Absolute or relative source file path.
|
|
17
|
+
* @param compilerOptions User-provided MDX compiler options.
|
|
18
|
+
* @returns The compile format that should be passed to `@mdx-js/mdx`.
|
|
19
|
+
*/
|
|
20
|
+
function resolveCompileFormat(filePath: string, compilerOptions?: CompileOptions): CompileOptions['format'] {
|
|
21
|
+
const configuredFormat = compilerOptions?.format;
|
|
22
|
+
|
|
23
|
+
if (configuredFormat && configuredFormat !== 'detect') {
|
|
24
|
+
return configuredFormat;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return path.extname(filePath).toLowerCase() === '.md' ? 'mdx' : configuredFormat;
|
|
28
|
+
}
|
|
29
|
+
|
|
8
30
|
export function createMdxLoaderPlugin(compilerOptions?: CompileOptions): EcoBuildPlugin {
|
|
9
31
|
const mdxExtensions = compilerOptions?.mdxExtensions ?? ['.mdx'];
|
|
10
32
|
const mdExtensions = compilerOptions?.mdExtensions ?? ['.md'];
|
|
@@ -22,15 +44,16 @@ export function createMdxLoaderPlugin(compilerOptions?: CompileOptions): EcoBuil
|
|
|
22
44
|
|
|
23
45
|
const compiled = await compile(file, {
|
|
24
46
|
...compilerOptions,
|
|
25
|
-
|
|
47
|
+
format: resolveCompileFormat(filePath, compilerOptions),
|
|
48
|
+
SourceMapGenerator: sourceMap.SourceMapGenerator,
|
|
26
49
|
});
|
|
27
50
|
|
|
28
|
-
const
|
|
51
|
+
const inlineSourceMap = compiled.map
|
|
29
52
|
? `\n//# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(compiled.map)).toString('base64')}\n`
|
|
30
53
|
: '';
|
|
31
54
|
|
|
32
55
|
return {
|
|
33
|
-
contents: `${String(compiled.value)}${
|
|
56
|
+
contents: `${String(compiled.value)}${inlineSourceMap}`,
|
|
34
57
|
loader: compilerOptions?.jsx ? 'jsx' : 'js',
|
|
35
58
|
resolveDir: path.dirname(args.path),
|
|
36
59
|
};
|
package/src/mdx.plugin.d.ts
CHANGED
|
@@ -24,6 +24,18 @@ export declare class MDXPlugin extends IntegrationPlugin<EcoPagesElement> {
|
|
|
24
24
|
private mdxLoaderPlugin;
|
|
25
25
|
constructor({ compilerOptions, ...options }?: MDXPluginConfig);
|
|
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
|
@@ -12,6 +12,18 @@ const defaultOptions = {
|
|
|
12
12
|
jsxRuntime: "automatic",
|
|
13
13
|
development: process.env.NODE_ENV === "development"
|
|
14
14
|
};
|
|
15
|
+
function splitMarkdownExtensions(extensions) {
|
|
16
|
+
const mdExtensions = [];
|
|
17
|
+
const mdxExtensions = [];
|
|
18
|
+
for (const extension of extensions) {
|
|
19
|
+
if (extension === ".md") {
|
|
20
|
+
mdExtensions.push(extension);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
mdxExtensions.push(extension);
|
|
24
|
+
}
|
|
25
|
+
return { mdExtensions, mdxExtensions };
|
|
26
|
+
}
|
|
15
27
|
class MDXPlugin extends IntegrationPlugin {
|
|
16
28
|
renderer;
|
|
17
29
|
compilerOptions;
|
|
@@ -22,7 +34,15 @@ class MDXPlugin extends IntegrationPlugin {
|
|
|
22
34
|
extensions: [".mdx"],
|
|
23
35
|
...options
|
|
24
36
|
});
|
|
25
|
-
const
|
|
37
|
+
const { mdExtensions, mdxExtensions } = splitMarkdownExtensions(this.extensions);
|
|
38
|
+
const finalCompilerOptions = deepMerge(
|
|
39
|
+
{
|
|
40
|
+
...defaultOptions,
|
|
41
|
+
mdxExtensions,
|
|
42
|
+
mdExtensions
|
|
43
|
+
},
|
|
44
|
+
compilerOptions
|
|
45
|
+
);
|
|
26
46
|
const jsxImportSource = finalCompilerOptions.jsxImportSource;
|
|
27
47
|
if (jsxImportSource === "react" || (jsxImportSource?.startsWith("react/") ?? false)) {
|
|
28
48
|
throw new Error(
|
|
@@ -39,8 +59,27 @@ class MDXPlugin extends IntegrationPlugin {
|
|
|
39
59
|
}
|
|
40
60
|
return [];
|
|
41
61
|
}
|
|
42
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Materializes the MDX loader once so config-time sealing and runtime setup
|
|
64
|
+
* can share the same loader instance.
|
|
65
|
+
*/
|
|
66
|
+
ensureLoaderPlugin() {
|
|
67
|
+
if (this.mdxLoaderPlugin) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
43
70
|
this.mdxLoaderPlugin = createMdxLoaderPlugin(this.compilerOptions);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Prepares the MDX loader contribution before config build seals the manifest.
|
|
74
|
+
*/
|
|
75
|
+
async prepareBuildContributions() {
|
|
76
|
+
this.ensureLoaderPlugin();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Runs runtime-only MDX setup after build contributions are already prepared.
|
|
80
|
+
*/
|
|
81
|
+
async setup() {
|
|
82
|
+
this.ensureLoaderPlugin();
|
|
44
83
|
await super.setup();
|
|
45
84
|
}
|
|
46
85
|
}
|
package/src/mdx.plugin.ts
CHANGED
|
@@ -26,6 +26,31 @@ const defaultOptions: CompileOptions = {
|
|
|
26
26
|
development: process.env.NODE_ENV === 'development',
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Splits configured markdown extensions into the two buckets understood by the
|
|
31
|
+
* MDX loader.
|
|
32
|
+
*
|
|
33
|
+
* `.mdx` remains the native MDX extension list. `.md` is special: it is only
|
|
34
|
+
* treated as MDX when a caller explicitly opts it into the pipeline, so we keep
|
|
35
|
+
* it separate rather than hiding that behavior inside a pair of constructor
|
|
36
|
+
* filters.
|
|
37
|
+
*/
|
|
38
|
+
function splitMarkdownExtensions(extensions: string[]): Pick<CompileOptions, 'mdExtensions' | 'mdxExtensions'> {
|
|
39
|
+
const mdExtensions: string[] = [];
|
|
40
|
+
const mdxExtensions: string[] = [];
|
|
41
|
+
|
|
42
|
+
for (const extension of extensions) {
|
|
43
|
+
if (extension === '.md') {
|
|
44
|
+
mdExtensions.push(extension);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
mdxExtensions.push(extension);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { mdExtensions, mdxExtensions };
|
|
52
|
+
}
|
|
53
|
+
|
|
29
54
|
/**
|
|
30
55
|
* The MDX plugin class
|
|
31
56
|
* This plugin provides support for MDX components in Ecopages.
|
|
@@ -46,7 +71,16 @@ export class MDXPlugin extends IntegrationPlugin<EcoPagesElement> {
|
|
|
46
71
|
...options,
|
|
47
72
|
});
|
|
48
73
|
|
|
49
|
-
const
|
|
74
|
+
const { mdExtensions, mdxExtensions } = splitMarkdownExtensions(this.extensions);
|
|
75
|
+
|
|
76
|
+
const finalCompilerOptions = deepMerge(
|
|
77
|
+
{
|
|
78
|
+
...defaultOptions,
|
|
79
|
+
mdxExtensions,
|
|
80
|
+
mdExtensions,
|
|
81
|
+
},
|
|
82
|
+
compilerOptions,
|
|
83
|
+
);
|
|
50
84
|
const jsxImportSource = finalCompilerOptions.jsxImportSource;
|
|
51
85
|
|
|
52
86
|
if (jsxImportSource === 'react' || (jsxImportSource?.startsWith('react/') ?? false)) {
|
|
@@ -69,8 +103,30 @@ export class MDXPlugin extends IntegrationPlugin<EcoPagesElement> {
|
|
|
69
103
|
return [];
|
|
70
104
|
}
|
|
71
105
|
|
|
72
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Materializes the MDX loader once so config-time sealing and runtime setup
|
|
108
|
+
* can share the same loader instance.
|
|
109
|
+
*/
|
|
110
|
+
private ensureLoaderPlugin(): void {
|
|
111
|
+
if (this.mdxLoaderPlugin) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
73
115
|
this.mdxLoaderPlugin = createMdxLoaderPlugin(this.compilerOptions);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Prepares the MDX loader contribution before config build seals the manifest.
|
|
120
|
+
*/
|
|
121
|
+
override async prepareBuildContributions(): Promise<void> {
|
|
122
|
+
this.ensureLoaderPlugin();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Runs runtime-only MDX setup after build contributions are already prepared.
|
|
127
|
+
*/
|
|
128
|
+
override async setup(): Promise<void> {
|
|
129
|
+
this.ensureLoaderPlugin();
|
|
74
130
|
await super.setup();
|
|
75
131
|
}
|
|
76
132
|
}
|