recma-mdx-change-imports v1.1.2
recma-mdx-change-imports
This package is a unified (recma) plugin that converts import declarations with relative path into variable declarations of string URLs for assets and media in the compiled MDX source.
unified is a project that transforms content with abstract syntax trees (ASTs) using the new parser micromark. recma adds support for producing a javascript code by transforming esast which stands for Ecma Script Abstract Syntax Tree (AST) that is used in production of compiled source for the MDX.
When should I use this?
Use this plugin to enable direct asset URL resolution in MDX documents.
recma-mdx-change-imports
transforms relative paths into string URLs for asset/media imports, ensuring they are correctly resolved in the compiled MDX output.
It resolves asset relative paths using the provided pathname
option.
Assume the option pathname
is data/articles
.
For example, it transforms:
import imgUrl from "./image.png";
into
const imgUrl = "/data/articles/image.png";
It supports ../
to resolve paths to parent and sibling directories. So it can transforms:
import imgUrl from "../blog-assets/image.png";
into
const imgUrl = "/data/blog-assets/image.png";
Using this plugin, you can write content in MDX like this:
import imgUrl from "./image.png";
<img src={imgUrl} alt="Example image" />
Otherwise, you probably get an error about unknown file extension .png
.
Installation
This package is suitable for ESM only. In Node.js (version 18+), install with npm:
npm install recma-mdx-change-imports
or
yarn add recma-mdx-change-imports
Usage
Say we have the following file, example.mdx
,
## Title
import imgUrl from "./image.png";
<img src={imgUrl} alt="Example image" />
And our module, example.js
, looks as follows:
import { read } from "to-vfile";
import { compile } from "@mdx-js/mdx";
import recmaMdxChangeImports from "recma-mdx-change-imports";
main();
async function main() {
const source = await read("example.mdx");
const compiledSource = await compile(source, {
outputFormat: "function-body",
recmaPlugins: [[recmaMdxChangeImports, {pathname: "data/articles"}]],
});
return String(compiledSource);
}
Now, running node example.js
produces the compiled source
below:
"use strict";
const {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];
const _importMetaUrl = arguments[0].baseUrl;
if (!_importMetaUrl) throw new Error("Unexpected missing \`options.baseUrl\` needed to support \`export … from\`, \`import\`, or \`import.meta.url\` when generating \`function-body\`");
- const {default: imgUrl} = await import(_resolveDynamicMdxSpecifier("./image.png"));
+ const imgUrl = "/data/articles/image.png";
function _createMdxContent(props) {
const _components = {
h2: "h2",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h2, {
children: "Title"
}), "\\n", "\\n", _jsx("img", {
src: imgUrl,
alt: "Example image"
})]
});
}
function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
return {
default: MDXContent
};
function _resolveDynamicMdxSpecifier(d) {
if (typeof d !== "string") return d;
try {
new URL(d);
return d;
} catch {}
if (d.startsWith("/") || d.startsWith("./") || d.startsWith("../")) return new URL(d, _importMetaUrl).href;
return d;
}
Options
All options are optional and have no default value which is undefined
.
export type ChangeImportsOptions = {
pathname?: string; // default is undefined
baseUrl?: string; // default is undefined
};
pathname
It is a string
option that serves as a base path for resolving relative paths, ultimately determining the final asset path as resolved path.
If no pathname
is provided, the plugin simply removes ./
and ../
from the relative path without proper resolution. Therefore, it is recommended to always specify a pathname
; otherwise, the plugin cannot determine the correct current, parent, or ancestor directories.
Similarly, if a pathname
is provided but the resolved path exceeds the root, ./
and ../
are removed in the same way, preventing invalid path resolution.
use(recmaMdxChangeImports, {pathname: "data/articles"} as ChangeImportsOptions);
Let's assume we have the import statement import imgUrl from "./image.png"
in an MDX file.
With the option pathname
, now the statement in the compiled MDX source is going to be:
const imgUrl = "/data/articles/image.png";
baseUrl
It is a string
option which is for resolving absolute path (file:///
) produced by compilation of MDX.
recma-mdx-change-imports
utilizes the baseUrl
for deriving relative path from the absolute path, if necessary.
baseUrl
should be the same with baseUrl
in @mdx-js/mdx
. In some cases, you may not need to provide a baseUrl
, because the compile
of @mdx-js/mdx
may not produce absolute path according to the options provided. If you see the image doesn't display and the src
property consists something ../file:///..
then you need to provide the same baseUrl
(mostly import.meta.url
) to the plugin.
use(recmaMdxChangeImports, {pathname: "data/articles", baseUrl: import.meta.url} as ChangeImportsOptions);
Syntax tree
This plugin only modifies the ESAST (Ecma Script Abstract Syntax Tree) as explained.
Types
This package is fully typed with TypeScript. The plugin options is exported as ChangeImportsOptions
.
Compatibility
This plugin works with unified
version 6+. It is compatible with mdx
version 3+.
Security
Use of recma-mdx-change-imports
does not involve user content so there are no openings for cross-site scripting (XSS) attacks.
My Plugins
I like to contribute the Unified / Remark / MDX ecosystem, so I recommend you to have a look my plugins.
My Remark Plugins
remark-flexible-code-titles
– Remark plugin to add titles or/and containers for the code blocks with customizable propertiesremark-flexible-containers
– Remark plugin to add custom containers with customizable properties in markdownremark-ins
– Remark plugin to addins
element in markdownremark-flexible-paragraphs
– Remark plugin to add custom paragraphs with customizable properties in markdownremark-flexible-markers
– Remark plugin to add custommark
element with customizable properties in markdownremark-flexible-toc
– Remark plugin to expose the table of contents viavfile.data
or via an option referenceremark-mdx-remove-esm
– Remark plugin to remove import and/or export statements (mdxjsEsm)
My Rehype Plugins
rehype-pre-language
– Rehype plugin to add language information as a property topre
elementrehype-highlight-code-lines
– Rehype plugin to add line numbers to code blocks and allow highlighting of desired code lines
My Recma Plugins
recma-mdx-escape-missing-components
– Recma plugin to set the default value() => null
for the Components in MDX in case of missing or not provided so as not to throw an errorrecma-mdx-change-props
– Recma plugin to change theprops
parameter into the_props
in thefunction _createMdxContent(props) {/* */}
in the compiled source in order to be able to use{props.foo}
like expressions. It is useful for thenext-mdx-remote
ornext-mdx-remote-client
users innextjs
applications.recma-mdx-change-imports
– Recma plugin to convert import declarations for assets and media with relative links into variable declarations with string URLs, enabling direct asset URL resolution in compiled MDX.recma-mdx-import-media
– Recma plugin to turn media relative paths into import declarations for both markdown and html syntax in MDX.
License
MIT License © ipikuka