@ecopages/ecopages-jsx 0.2.0-beta.1 → 0.2.0-beta.11

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.
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@ecopages/mdx-core",
3
+ "version": "0.2.0-beta.11",
4
+ "description": "Shared MDX loader utilities for Ecopages integrations",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "default": "./src/index.js",
10
+ "types": "./src/index.d.ts"
11
+ }
12
+ },
13
+ "peerDependencies": {
14
+ "@ecopages/core": "0.2.0-beta.11",
15
+ "@mdx-js/mdx": "^3.1.0"
16
+ },
17
+ "dependencies": {
18
+ "source-map": "^0.7.6",
19
+ "vfile": "^6.0.3"
20
+ },
21
+ "types": "./src/index.d.ts"
22
+ }
@@ -0,0 +1,2 @@
1
+ export { createMdxLoaderPlugin, type CreateMdxLoaderPluginOptions } from './mdx-loader-plugin.js';
2
+ export { appendMdxExtensions, createMdxExtensionFilter, mergePluginLists, resolveCompileFormat, resolveLoaderExtensions, resolveMdxCompilerOptions, type MdxCompilerOptionsInput, } from './mdx-utils.js';
@@ -0,0 +1,18 @@
1
+ import { createMdxLoaderPlugin } from "./mdx-loader-plugin.js";
2
+ import {
3
+ appendMdxExtensions,
4
+ createMdxExtensionFilter,
5
+ mergePluginLists,
6
+ resolveCompileFormat,
7
+ resolveLoaderExtensions,
8
+ resolveMdxCompilerOptions
9
+ } from "./mdx-utils.js";
10
+ export {
11
+ appendMdxExtensions,
12
+ createMdxExtensionFilter,
13
+ createMdxLoaderPlugin,
14
+ mergePluginLists,
15
+ resolveCompileFormat,
16
+ resolveLoaderExtensions,
17
+ resolveMdxCompilerOptions
18
+ };
@@ -0,0 +1,11 @@
1
+ import type { EcoBuildPlugin } from '@ecopages/core/plugins/integration-plugin';
2
+ import { type CompileOptions } from '@mdx-js/mdx';
3
+ export interface CreateMdxLoaderPluginOptions {
4
+ name: string;
5
+ compilerOptions?: CompileOptions;
6
+ extensions?: string[];
7
+ defaultMdExtensions?: string[];
8
+ includeSourceMap?: boolean;
9
+ loader?: 'js' | 'jsx';
10
+ }
11
+ export declare function createMdxLoaderPlugin(options: CreateMdxLoaderPluginOptions): EcoBuildPlugin;
@@ -0,0 +1,44 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { compile } from "@mdx-js/mdx";
4
+ import sourceMap from "source-map";
5
+ import { VFile } from "vfile";
6
+ import { createMdxExtensionFilter, resolveCompileFormat, resolveLoaderExtensions } from "./mdx-utils.js";
7
+ function createMdxLoaderPlugin(options) {
8
+ const {
9
+ name,
10
+ compilerOptions,
11
+ extensions = resolveLoaderExtensions(compilerOptions, {
12
+ defaultMdExtensions: options.defaultMdExtensions
13
+ }),
14
+ includeSourceMap = true,
15
+ loader = compilerOptions?.jsx ? "jsx" : "js"
16
+ } = options;
17
+ const filter = createMdxExtensionFilter(extensions, { allowQueryString: true });
18
+ return {
19
+ name,
20
+ setup(build) {
21
+ build.onLoad({ filter }, async (args) => {
22
+ const filePath = args.path.includes("?") ? args.path.split("?")[0] : args.path;
23
+ const source = await readFile(filePath, "utf-8");
24
+ const file = new VFile({ path: filePath, value: source });
25
+ const compiled = await compile(file, {
26
+ ...compilerOptions,
27
+ format: resolveCompileFormat(filePath, compilerOptions),
28
+ SourceMapGenerator: sourceMap.SourceMapGenerator
29
+ });
30
+ const inlineSourceMap = includeSourceMap && compiled.map ? `
31
+ //# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(compiled.map)).toString("base64")}
32
+ ` : "";
33
+ return {
34
+ contents: `${String(compiled.value)}${inlineSourceMap}`,
35
+ loader,
36
+ resolveDir: path.dirname(args.path)
37
+ };
38
+ });
39
+ }
40
+ };
41
+ }
42
+ export {
43
+ createMdxLoaderPlugin
44
+ };
@@ -0,0 +1,29 @@
1
+ import type { CompileOptions } from '@mdx-js/mdx';
2
+ export interface MdxCompilerOptionsInput {
3
+ compilerOptions?: CompileOptions;
4
+ remarkPlugins?: CompileOptions['remarkPlugins'];
5
+ rehypePlugins?: CompileOptions['rehypePlugins'];
6
+ recmaPlugins?: CompileOptions['recmaPlugins'];
7
+ }
8
+ export declare const mergePluginLists: <T>(...lists: Array<readonly T[] | null | undefined>) => T[] | undefined;
9
+ export declare const appendMdxExtensions: (target: string[], mdxExtensions: string[]) => void;
10
+ export declare const createMdxExtensionFilter: (extensions: string[], options?: {
11
+ allowQueryString?: boolean;
12
+ }) => RegExp;
13
+ export declare function resolveLoaderExtensions(compilerOptions?: CompileOptions, options?: {
14
+ defaultMdExtensions?: string[];
15
+ }): string[];
16
+ /**
17
+ * Resolves the MDX parser mode for a source file.
18
+ *
19
+ * Files with a `.md` extension must be forced into `mdx` mode when the caller
20
+ * explicitly opts them into the MDX pipeline. Leaving the compiler in `detect`
21
+ * mode would treat `.md` files as plain markdown, causing top-level ESM such as
22
+ * `import` and `export` to render as text instead of being compiled.
23
+ */
24
+ export declare function resolveCompileFormat(filePath: string, compilerOptions?: CompileOptions): CompileOptions['format'];
25
+ export declare function resolveMdxCompilerOptions(mdxOptions: MdxCompilerOptionsInput, options: {
26
+ jsxImportSource: string;
27
+ jsxRuntime?: CompileOptions['jsxRuntime'];
28
+ defaults?: CompileOptions;
29
+ }): CompileOptions;
@@ -0,0 +1,55 @@
1
+ import path from "node:path";
2
+ const mergePluginLists = (...lists) => {
3
+ const merged = lists.flatMap((list) => list ? [...list] : []);
4
+ return merged.length > 0 ? merged : void 0;
5
+ };
6
+ const appendMdxExtensions = (target, mdxExtensions) => {
7
+ for (const extension of mdxExtensions) {
8
+ if (!target.includes(extension)) {
9
+ target.push(extension);
10
+ }
11
+ }
12
+ };
13
+ const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14
+ const createMdxExtensionFilter = (extensions, options) => {
15
+ const escaped = extensions.map(escapeRegex);
16
+ const suffix = options?.allowQueryString ? "(\\?.*)?$" : "$";
17
+ return new RegExp(`(${escaped.join("|")})${suffix}`);
18
+ };
19
+ function resolveLoaderExtensions(compilerOptions, options) {
20
+ const mdxExtensions = compilerOptions?.mdxExtensions ?? [".mdx"];
21
+ const mdExtensions = compilerOptions?.mdExtensions ?? options?.defaultMdExtensions ?? [];
22
+ return [...mdxExtensions, ...mdExtensions];
23
+ }
24
+ function resolveCompileFormat(filePath, compilerOptions) {
25
+ const configuredFormat = compilerOptions?.format;
26
+ if (configuredFormat && configuredFormat !== "detect") {
27
+ return configuredFormat;
28
+ }
29
+ return path.extname(filePath).toLowerCase() === ".md" ? "mdx" : configuredFormat;
30
+ }
31
+ function resolveMdxCompilerOptions(mdxOptions, options) {
32
+ const { compilerOptions, remarkPlugins, rehypePlugins, recmaPlugins } = mdxOptions;
33
+ const resolved = {
34
+ ...options.defaults,
35
+ ...compilerOptions,
36
+ jsxImportSource: options.jsxImportSource,
37
+ jsxRuntime: options.jsxRuntime ?? "automatic",
38
+ development: process.env.NODE_ENV === "development"
39
+ };
40
+ const mergedRemark = mergePluginLists(compilerOptions?.remarkPlugins, remarkPlugins);
41
+ const mergedRehype = mergePluginLists(compilerOptions?.rehypePlugins, rehypePlugins);
42
+ const mergedRecma = mergePluginLists(compilerOptions?.recmaPlugins, recmaPlugins);
43
+ if (mergedRemark) resolved.remarkPlugins = mergedRemark;
44
+ if (mergedRehype) resolved.rehypePlugins = mergedRehype;
45
+ if (mergedRecma) resolved.recmaPlugins = mergedRecma;
46
+ return resolved;
47
+ }
48
+ export {
49
+ appendMdxExtensions,
50
+ createMdxExtensionFilter,
51
+ mergePluginLists,
52
+ resolveCompileFormat,
53
+ resolveLoaderExtensions,
54
+ resolveMdxCompilerOptions
55
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecopages/ecopages-jsx",
3
- "version": "0.2.0-beta.1",
3
+ "version": "0.2.0-beta.11",
4
4
  "description": "JSX integration plugin for Ecopages",
5
5
  "keywords": [
6
6
  "ecopages",
@@ -27,15 +27,19 @@
27
27
  },
28
28
  "repository": {
29
29
  "type": "git",
30
- "url": "https://github.com/ecopages/ecopages.git",
30
+ "url": "git+https://github.com/ecopages/ecopages.git",
31
31
  "directory": "packages/integrations/ecopages-jsx"
32
32
  },
33
+ "bundleDependencies": [
34
+ "@ecopages/mdx-core"
35
+ ],
33
36
  "dependencies": {
37
+ "@ecopages/mdx-core": "0.2.0-beta.11",
34
38
  "@mdx-js/mdx": "^3.1.1",
35
39
  "vfile": "^6.0.3"
36
40
  },
37
41
  "peerDependencies": {
38
- "@ecopages/core": "0.2.0-beta.1",
42
+ "@ecopages/core": "0.2.0-beta.11",
39
43
  "@ecopages/jsx": "0.3.0-alpha.25",
40
44
  "@ecopages/radiant": "0.3.0-alpha.25"
41
45
  }
@@ -1,6 +1,7 @@
1
1
  import type { CompileOptions } from '@mdx-js/mdx';
2
2
  import type { EcoComponent, EcoComponentConfig, EcoFunctionComponent, EcoPageFile, GetMetadata } from '@ecopages/core';
3
3
  import type { EcoBuildPlugin } from '@ecopages/core/plugins/integration-plugin';
4
+ import { appendMdxExtensions, createMdxExtensionFilter } from '@ecopages/mdx-core';
4
5
  import type { JsxRenderable } from '@ecopages/jsx';
5
6
  import type { EcopagesJsxMdxCompileOptions, EcopagesJsxMdxOptions } from './ecopages-jsx.types.js';
6
7
  export type ResolvedMdxCompileOptions = EcopagesJsxMdxCompileOptions & Pick<CompileOptions, 'jsxImportSource' | 'jsxRuntime'>;
@@ -10,10 +11,7 @@ export type EcopagesJsxMdxPageModule = EcoPageFile<{
10
11
  layout?: EcoComponent;
11
12
  getMetadata?: GetMetadata;
12
13
  }>;
13
- export declare const createMdxExtensionFilter: (extensions: string[], options?: {
14
- allowQueryString?: boolean;
15
- }) => RegExp;
16
- export declare const appendMdxExtensions: (target: string[], mdxExtensions: string[]) => void;
14
+ export { appendMdxExtensions, createMdxExtensionFilter };
17
15
  export declare const resolveMdxCompilerOptions: (mdxOptions: EcopagesJsxMdxOptions) => ResolvedMdxCompileOptions;
18
16
  export declare const createMdxLoaderPlugin: (compilerOptions: ResolvedMdxCompileOptions, extensions: string[]) => EcoBuildPlugin;
19
17
  export declare const registerBunMdxPlugin: (compilerOptions: ResolvedMdxCompileOptions, extensions: string[]) => Promise<void>;
@@ -1,62 +1,27 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import path from "node:path";
3
2
  import { rapidhash } from "@ecopages/core/hash";
3
+ import {
4
+ appendMdxExtensions,
5
+ createMdxExtensionFilter,
6
+ createMdxLoaderPlugin as createMdxLoaderPluginCore,
7
+ resolveMdxCompilerOptions as resolveMdxCompilerOptionsCore
8
+ } from "@ecopages/mdx-core";
4
9
  import { VFile } from "vfile";
5
10
  import { ECOPAGES_JSX_PLUGIN_NAME } from "./ecopages-jsx.constants.js";
6
- const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7
- const mergePluginLists = (...lists) => {
8
- const merged = lists.flatMap((list) => list ? [...list] : []);
9
- return merged.length > 0 ? merged : void 0;
10
- };
11
- const createMdxExtensionFilter = (extensions, options) => {
12
- const escaped = extensions.map(escapeRegex);
13
- const suffix = options?.allowQueryString ? "(\\?.*)?$" : "$";
14
- return new RegExp(`(${escaped.join("|")})${suffix}`);
15
- };
16
- const appendMdxExtensions = (target, mdxExtensions) => {
17
- for (const ext of mdxExtensions) {
18
- if (!target.includes(ext)) {
19
- target.push(ext);
20
- }
21
- }
22
- };
23
- const resolveMdxCompilerOptions = (mdxOptions) => {
24
- const { compilerOptions, remarkPlugins, rehypePlugins, recmaPlugins } = mdxOptions;
25
- const resolved = {
11
+ const resolveMdxCompilerOptions = (mdxOptions) => resolveMdxCompilerOptionsCore(mdxOptions, {
12
+ jsxImportSource: "@ecopages/jsx",
13
+ defaults: {
26
14
  format: "detect",
27
- outputFormat: "program",
28
- ...compilerOptions,
29
- jsxImportSource: "@ecopages/jsx",
30
- jsxRuntime: "automatic",
31
- development: process.env.NODE_ENV === "development"
32
- };
33
- const mergedRemark = mergePluginLists(compilerOptions?.remarkPlugins, remarkPlugins);
34
- const mergedRehype = mergePluginLists(compilerOptions?.rehypePlugins, rehypePlugins);
35
- const mergedRecma = mergePluginLists(compilerOptions?.recmaPlugins, recmaPlugins);
36
- if (mergedRemark) resolved.remarkPlugins = mergedRemark;
37
- if (mergedRehype) resolved.rehypePlugins = mergedRehype;
38
- if (mergedRecma) resolved.recmaPlugins = mergedRecma;
39
- return resolved;
40
- };
41
- const createMdxLoaderPlugin = (compilerOptions, extensions) => {
42
- const filter = createMdxExtensionFilter(extensions, { allowQueryString: true });
43
- return {
44
- name: "ecopages-jsx-mdx-loader",
45
- setup(build) {
46
- build.onLoad({ filter }, async (args) => {
47
- const { compile } = await import("@mdx-js/mdx");
48
- const filePath = args.path.includes("?") ? args.path.split("?")[0] : args.path;
49
- const source = await readFile(filePath, "utf-8");
50
- const compiled = await compile(new VFile({ value: source, path: filePath }), compilerOptions);
51
- return {
52
- contents: String(compiled.value),
53
- loader: "js",
54
- resolveDir: path.dirname(filePath)
55
- };
56
- });
57
- }
58
- };
59
- };
15
+ outputFormat: "program"
16
+ }
17
+ });
18
+ const createMdxLoaderPlugin = (compilerOptions, extensions) => createMdxLoaderPluginCore({
19
+ name: "ecopages-jsx-mdx-loader",
20
+ compilerOptions,
21
+ extensions,
22
+ includeSourceMap: false,
23
+ loader: "js"
24
+ });
60
25
  const registerBunMdxPlugin = async (compilerOptions, extensions) => {
61
26
  if (typeof Bun === "undefined") {
62
27
  return;
@@ -22,13 +22,6 @@ export declare class EcopagesJsxRenderer extends IntegrationRenderer<JsxRenderab
22
22
  * elements and queued foreign subtrees contribute assets to the same frame.
23
23
  */
24
24
  private renderQueuedForeignSubtreeChildren;
25
- /**
26
- * Resolves queued foreign subtrees after JSX has been stringified.
27
- *
28
- * JSX content needs one extra render pass because child foreign subtrees may emit
29
- * additional browser assets while also replacing placeholder tokens.
30
- */
31
- private resolveOwnedForeignSubtreeHtml;
32
25
  protected createForeignChildRuntime(options: {
33
26
  renderInput: ComponentRenderInput;
34
27
  rendererCache: Map<string, IntegrationRenderer<any>>;
@@ -61,24 +61,6 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
61
61
  html
62
62
  };
63
63
  }
64
- /**
65
- * Resolves queued foreign subtrees after JSX has been stringified.
66
- *
67
- * JSX content needs one extra render pass because child foreign subtrees may emit
68
- * additional browser assets while also replacing placeholder tokens.
69
- */
70
- async resolveOwnedForeignSubtreeHtml(html, runtimeContext) {
71
- return this.foreignSubtreeExecutionService.resolveQueuedHtml({
72
- currentIntegrationName: this.name,
73
- html,
74
- runtimeContext,
75
- queueLabel: "Ecopages JSX",
76
- getOwningRenderer: (integrationName, rendererCache) => this.getIntegrationRendererForName(integrationName, rendererCache),
77
- applyAttributesToFirstElement: (resolvedHtml, attributes) => this.htmlTransformer.applyAttributesToFirstElement(resolvedHtml, attributes),
78
- dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets),
79
- renderQueuedChildren: async (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => this.renderQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken)
80
- });
81
- }
82
64
  createForeignChildRuntime(options) {
83
65
  const runtime = super.createForeignChildRuntime(options);
84
66
  const interceptForeignChild = runtime.interceptForeignChild;
@@ -165,11 +147,11 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
165
147
  };
166
148
  const content = await this.withCustomElementRenderHook(() => component(componentProps));
167
149
  const rendered = await this.renderJsx(content);
168
- const queuedForeignSubtreeResolution = await this.resolveOwnedForeignSubtreeHtml(
150
+ const queuedForeignSubtreeResolution = await this.resolveQueuedForeignSubtreeHtml(
169
151
  rendered.html,
170
- this.getQueuedForeignSubtreeResolutionContext(
171
- input
172
- )
152
+ this.getQueuedForeignSubtreeResolutionContext(input),
153
+ (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => this.renderQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken),
154
+ "Ecopages JSX"
173
155
  );
174
156
  const componentAssets = input.component.config?.dependencies && typeof this.assetProcessingService?.processDependencies === "function" ? await this.processComponentDependencies([input.component]) : [];
175
157
  const assets = this.htmlTransformer.dedupeProcessedAssets([