recma-mdx-html-override 1.0.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 +21 -0
- package/README.md +201 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.js +186 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -0
- package/package.json +85 -0
- package/src/index.ts +243 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ipikuka
|
|
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
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# recma-mdx-html-override
|
|
2
|
+
|
|
3
|
+
[![npm version][badge-npm-version]][url-npm-package]
|
|
4
|
+
[![npm downloads][badge-npm-download]][url-npm-package]
|
|
5
|
+
[![publish to npm][badge-publish-to-npm]][url-publish-github-actions]
|
|
6
|
+
[![code-coverage][badge-codecov]][url-codecov]
|
|
7
|
+
[![type-coverage][badge-type-coverage]][url-github-package]
|
|
8
|
+
[![typescript][badge-typescript]][url-typescript]
|
|
9
|
+
[![license][badge-license]][url-license]
|
|
10
|
+
|
|
11
|
+
This package is a **[unified][unified]** (**[recma][recma]**) plugin **that ensures selected html raw elements overridable via MDXComponents in MDX**.
|
|
12
|
+
|
|
13
|
+
**[unified][unified]** is a project that transforms content with abstract syntax trees (ASTs) using the new parser **[micromark][micromark]**. **[recma][recma]** adds support for producing a javascript code by transforming **[esast][esast]** which stands for Ecma Script Abstract Syntax Tree (AST) that is used in production of compiled source for the **[MDX][MDX]**.
|
|
14
|
+
|
|
15
|
+
## When should I use this?
|
|
16
|
+
|
|
17
|
+
**Use this plugin to be able to override selected html raw elements via MDXComponents**.
|
|
18
|
+
|
|
19
|
+
You can find the keys (JSX identifiers, `wrapper` and html tags) can be passed in MDXComponents and the rules whether a key is a `Literal` or a `Reference to an Identifier` in [@mdx-js/mdx docs](https://mdxjs.com/docs/using-mdx/#components).
|
|
20
|
+
|
|
21
|
+
**`recma-mdx-html-override`** focuses the `Literal` ones (the names that start with a lowercase or is not a valid JS identifier) to make them overridable via MDXComponents.
|
|
22
|
+
|
|
23
|
+
Basically, **`recma-mdx-html-override`** changes the `Literal` parameters into **`_components.[literal]`** in the `jsx`/`jsxs` call expressions making approppriate changes in the compiled source; and ensures them overridable via mdx components.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
This package is suitable for ESM only. In Node.js (version 18+), install with npm:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install recma-mdx-html-override
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
or
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
yarn add recma-mdx-html-override
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
Say we have the following file, `example.mdx`,
|
|
42
|
+
|
|
43
|
+
```mdx
|
|
44
|
+
# Hi
|
|
45
|
+
|
|
46
|
+
<img src="image.png" alt="picture" />
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
And our module, `example.js`, looks as follows:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
import { read } from "to-vfile";
|
|
53
|
+
import { compile } from "@mdx-js/mdx";
|
|
54
|
+
import recmaMdxHtmlOverride from "recma-mdx-html-override";
|
|
55
|
+
|
|
56
|
+
main();
|
|
57
|
+
|
|
58
|
+
async function main() {
|
|
59
|
+
const source = await read("example.mdx");
|
|
60
|
+
|
|
61
|
+
const compiledSource = await compile(source, {
|
|
62
|
+
recmaPlugins: [[recmaMdxHtmlOverride, {tags: "img"}]],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return String(compiledSource);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Now, running `node example.js` produces the `compiled source` like below:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
function _createMdxContent(props) {
|
|
73
|
+
const _components = {
|
|
74
|
+
h1: "h1",
|
|
75
|
+
+ img: "img",
|
|
76
|
+
...props.components
|
|
77
|
+
};
|
|
78
|
+
return _jsxs(_Fragment, {
|
|
79
|
+
children: [_jsx(_components.h1, {
|
|
80
|
+
children: "Hi"
|
|
81
|
+
- }), "\\n", _jsx("img", {
|
|
82
|
+
+ }), "\\n", _jsx(_components.img, {
|
|
83
|
+
src: "image.png",
|
|
84
|
+
alt: "picture"
|
|
85
|
+
})]
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
And, this provides us to override **`img`** components via mdx components `{ img: () => {/* */} }`
|
|
91
|
+
|
|
92
|
+
## Options
|
|
93
|
+
|
|
94
|
+
There is one option, which is `undefined` by default.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
export type HtmlOverrideOptions = {
|
|
98
|
+
tags?: string | string[]; // default is undefined
|
|
99
|
+
};
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Tags
|
|
103
|
+
|
|
104
|
+
It is a **`string | string[]`** option to set the tag names of html raw elements to be made overridable in MDX.
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
use(recmaMdxHtmlOverride, {tags: "video"} as HtmlOverrideOptions);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Now, `<video />` html elements in MDX will be overridable via mdx components.
|
|
111
|
+
|
|
112
|
+
## Syntax tree
|
|
113
|
+
|
|
114
|
+
This plugin only modifies the ESAST (Ecma Script Abstract Syntax Tree) as explained.
|
|
115
|
+
|
|
116
|
+
## Types
|
|
117
|
+
|
|
118
|
+
This package is fully typed with [TypeScript][url-typescript]. The plugin options is exported as `HtmlOverrideOptions`.
|
|
119
|
+
|
|
120
|
+
## Compatibility
|
|
121
|
+
|
|
122
|
+
This plugin works with `unified` version 6+. It is compatible with `mdx` version 3+.
|
|
123
|
+
|
|
124
|
+
## Security
|
|
125
|
+
|
|
126
|
+
Use of `recma-mdx-html-override` does not involve user content so there are no openings for cross-site scripting (XSS) attacks.
|
|
127
|
+
|
|
128
|
+
## My Plugins
|
|
129
|
+
|
|
130
|
+
I like to contribute the Unified / Remark / MDX ecosystem, so I recommend you to have a look my plugins.
|
|
131
|
+
|
|
132
|
+
### My Remark Plugins
|
|
133
|
+
|
|
134
|
+
- [`remark-flexible-code-titles`](https://www.npmjs.com/package/remark-flexible-code-titles)
|
|
135
|
+
– Remark plugin to add titles or/and containers for the code blocks with customizable properties
|
|
136
|
+
- [`remark-flexible-containers`](https://www.npmjs.com/package/remark-flexible-containers)
|
|
137
|
+
– Remark plugin to add custom containers with customizable properties in markdown
|
|
138
|
+
- [`remark-ins`](https://www.npmjs.com/package/remark-ins)
|
|
139
|
+
– Remark plugin to add `ins` element in markdown
|
|
140
|
+
- [`remark-flexible-paragraphs`](https://www.npmjs.com/package/remark-flexible-paragraphs)
|
|
141
|
+
– Remark plugin to add custom paragraphs with customizable properties in markdown
|
|
142
|
+
- [`remark-flexible-markers`](https://www.npmjs.com/package/remark-flexible-markers)
|
|
143
|
+
– Remark plugin to add custom `mark` element with customizable properties in markdown
|
|
144
|
+
- [`remark-flexible-toc`](https://www.npmjs.com/package/remark-flexible-toc)
|
|
145
|
+
– Remark plugin to expose the table of contents via `vfile.data` or via an option reference
|
|
146
|
+
- [`remark-mdx-remove-esm`](https://www.npmjs.com/package/remark-mdx-remove-esm)
|
|
147
|
+
– Remark plugin to remove import and/or export statements (mdxjsEsm)
|
|
148
|
+
|
|
149
|
+
### My Rehype Plugins
|
|
150
|
+
|
|
151
|
+
- [`rehype-pre-language`](https://www.npmjs.com/package/rehype-pre-language)
|
|
152
|
+
– Rehype plugin to add language information as a property to `pre` element
|
|
153
|
+
- [`rehype-highlight-code-lines`](https://www.npmjs.com/package/rehype-highlight-code-lines)
|
|
154
|
+
– Rehype plugin to add line numbers to code blocks and allow highlighting of desired code lines
|
|
155
|
+
- [`rehype-code-meta`](https://www.npmjs.com/package/rehype-code-meta)
|
|
156
|
+
– Rehype plugin to copy `code.data.meta` to `code.properties.metastring`
|
|
157
|
+
|
|
158
|
+
### My Recma Plugins
|
|
159
|
+
|
|
160
|
+
- [`recma-mdx-escape-missing-components`](https://www.npmjs.com/package/recma-mdx-escape-missing-components)
|
|
161
|
+
– 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 error
|
|
162
|
+
- [`recma-mdx-change-props`](https://www.npmjs.com/package/recma-mdx-change-props)
|
|
163
|
+
– Recma plugin to change the `props` parameter into the `_props` in the `function _createMdxContent(props) {/* */}` in the compiled source in order to be able to use `{props.foo}` like expressions. It is useful for the `next-mdx-remote` or `next-mdx-remote-client` users in `nextjs` applications.
|
|
164
|
+
- [`recma-mdx-change-imports`](https://www.npmjs.com/package/recma-mdx-change-imports)
|
|
165
|
+
– 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.
|
|
166
|
+
- [`recma-mdx-import-media`](https://www.npmjs.com/package/recma-mdx-import-media)
|
|
167
|
+
– Recma plugin to turn media relative paths into import declarations for both markdown and html syntax in MDX.
|
|
168
|
+
- [`recma-mdx-import-react`](https://www.npmjs.com/package/recma-mdx-import-react)
|
|
169
|
+
– Recma plugin to ensure getting `React` instance from the arguments and to make the runtime props `{React, jsx, jsxs, jsxDev, Fragment}` is available in the dynamically imported components in the compiled source of MDX.
|
|
170
|
+
- [`recma-mdx-html-override`](https://www.npmjs.com/package/recma-mdx-html-override)
|
|
171
|
+
– Recma plugin to ensure selected html raw elements overridable via mdx components in MDX.
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
[MIT License](./LICENSE) © ipikuka
|
|
176
|
+
|
|
177
|
+
[unified]: https://github.com/unifiedjs/unified
|
|
178
|
+
[micromark]: https://github.com/micromark/micromark
|
|
179
|
+
[recma]: https://mdxjs.com/docs/extending-mdx/#list-of-plugins
|
|
180
|
+
[esast]: https://github.com/syntax-tree/esast
|
|
181
|
+
[estree]: https://github.com/estree/estree
|
|
182
|
+
[MDX]: https://mdxjs.com/
|
|
183
|
+
|
|
184
|
+
[badge-npm-version]: https://img.shields.io/npm/v/recma-mdx-html-override
|
|
185
|
+
[badge-npm-download]:https://img.shields.io/npm/dt/recma-mdx-html-override
|
|
186
|
+
[url-npm-package]: https://www.npmjs.com/package/recma-mdx-html-override
|
|
187
|
+
[url-github-package]: https://github.com/ipikuka/recma-mdx-html-override
|
|
188
|
+
|
|
189
|
+
[badge-license]: https://img.shields.io/github/license/ipikuka/recma-mdx-html-override
|
|
190
|
+
[url-license]: https://github.com/ipikuka/recma-mdx-html-override/blob/main/LICENSE
|
|
191
|
+
|
|
192
|
+
[badge-publish-to-npm]: https://github.com/ipikuka/recma-mdx-html-override/actions/workflows/publish.yml/badge.svg
|
|
193
|
+
[url-publish-github-actions]: https://github.com/ipikuka/recma-mdx-html-override/actions/workflows/publish.yml
|
|
194
|
+
|
|
195
|
+
[badge-typescript]: https://img.shields.io/npm/types/recma-mdx-html-override
|
|
196
|
+
[url-typescript]: https://www.typescriptlang.org/
|
|
197
|
+
|
|
198
|
+
[badge-codecov]: https://codecov.io/gh/ipikuka/recma-mdx-html-override/graph/badge.svg?token=6UIKn4z8lc
|
|
199
|
+
[url-codecov]: https://codecov.io/gh/ipikuka/recma-mdx-html-override
|
|
200
|
+
|
|
201
|
+
[badge-type-coverage]: https://img.shields.io/badge/dynamic/json.svg?label=type-coverage&prefix=%E2%89%A5&suffix=%&query=$.typeCoverage.atLeast&uri=https%3A%2F%2Fraw.githubusercontent.com%2Fipikuka%2Frecma-mdx-html-override%2Fmaster%2Fpackage.json
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Plugin } from "unified";
|
|
2
|
+
import type { Program } from "estree";
|
|
3
|
+
export type HtmlOverrideOptions = {
|
|
4
|
+
tags?: string | string[];
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* It is a recma plugin which makes selected html raw elements overridable.
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
declare const plugin: Plugin<[HtmlOverrideOptions?], Program>;
|
|
12
|
+
export default plugin;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { CONTINUE, EXIT, SKIP, visit } from "estree-util-visit";
|
|
2
|
+
const DEFAULT_SETTINGS = {
|
|
3
|
+
tags: undefined,
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* It is a recma plugin which makes selected html raw elements overridable.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
const plugin = (options = {}) => {
|
|
11
|
+
const settings = Object.assign({}, DEFAULT_SETTINGS, options);
|
|
12
|
+
const componentMap = {};
|
|
13
|
+
let functionNode;
|
|
14
|
+
let functionPropsName = "props";
|
|
15
|
+
let targetVariableDeclarator;
|
|
16
|
+
function containsHyphen(name) {
|
|
17
|
+
return name.includes("-");
|
|
18
|
+
}
|
|
19
|
+
return (tree) => {
|
|
20
|
+
// console.dir(tree, { depth: 16 });
|
|
21
|
+
if (!settings.tags)
|
|
22
|
+
return;
|
|
23
|
+
// finds the function _createMdxContent(){}
|
|
24
|
+
visit(tree, (node, _, index) => {
|
|
25
|
+
if (index === undefined)
|
|
26
|
+
return;
|
|
27
|
+
if (node.type !== "FunctionDeclaration")
|
|
28
|
+
return SKIP;
|
|
29
|
+
if (node.id.name === "_createMdxContent") {
|
|
30
|
+
functionNode = node;
|
|
31
|
+
const param = node.params[0];
|
|
32
|
+
if (param.type === "Identifier") {
|
|
33
|
+
functionPropsName = param.name;
|
|
34
|
+
}
|
|
35
|
+
return EXIT;
|
|
36
|
+
}
|
|
37
|
+
/* istanbul ignore next */
|
|
38
|
+
return CONTINUE;
|
|
39
|
+
});
|
|
40
|
+
/* istanbul ignore next */
|
|
41
|
+
if (!functionNode)
|
|
42
|
+
return;
|
|
43
|
+
// trace call expressions to change _jsx("xxx", {}) to _jsx(_components.xxx, {})
|
|
44
|
+
visit(functionNode, (node) => {
|
|
45
|
+
if (node.type !== "CallExpression")
|
|
46
|
+
return CONTINUE;
|
|
47
|
+
if ("name" in node.callee) {
|
|
48
|
+
if (node.callee.name !== "_jsx" &&
|
|
49
|
+
node.callee.name !== "_jsxDEV" &&
|
|
50
|
+
node.callee.name !== "_jsxs") {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// First child of a CallExpression is a Literal or Identifier to a reference
|
|
55
|
+
const firstArgument = node.arguments[0];
|
|
56
|
+
if (firstArgument.type === "Literal" &&
|
|
57
|
+
typeof firstArgument.value === "string" &&
|
|
58
|
+
((typeof settings.tags === "string" && firstArgument.value === settings.tags) ||
|
|
59
|
+
settings.tags.includes(firstArgument.value))) {
|
|
60
|
+
node.arguments[0] = {
|
|
61
|
+
type: "MemberExpression",
|
|
62
|
+
object: { type: "Identifier", name: "_components" },
|
|
63
|
+
property: {
|
|
64
|
+
type: "Identifier",
|
|
65
|
+
name: containsHyphen(firstArgument.value)
|
|
66
|
+
? '"' + firstArgument.value + '"'
|
|
67
|
+
: firstArgument.value,
|
|
68
|
+
},
|
|
69
|
+
computed: containsHyphen(firstArgument.value),
|
|
70
|
+
optional: false,
|
|
71
|
+
};
|
|
72
|
+
if (!componentMap[firstArgument.value]) {
|
|
73
|
+
componentMap[firstArgument.value] = firstArgument.value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return CONTINUE;
|
|
77
|
+
});
|
|
78
|
+
// trace jsx elements to change <xxx /> to <__components.xxx />
|
|
79
|
+
visit(functionNode, (node) => {
|
|
80
|
+
if (node.type !== "JSXElement")
|
|
81
|
+
return CONTINUE;
|
|
82
|
+
// First child of a CallExpression is a Literal or Identifier to a reference
|
|
83
|
+
const openingElement = node.openingElement;
|
|
84
|
+
if (openingElement.name.type === "JSXIdentifier") {
|
|
85
|
+
const jsxIdentifier = openingElement.name;
|
|
86
|
+
if ((typeof settings.tags === "string" && jsxIdentifier.name === settings.tags) ||
|
|
87
|
+
settings.tags.includes(jsxIdentifier.name)) {
|
|
88
|
+
node.openingElement.name = {
|
|
89
|
+
type: "JSXMemberExpression",
|
|
90
|
+
object: { type: "JSXIdentifier", name: "_components" },
|
|
91
|
+
property: {
|
|
92
|
+
type: "JSXIdentifier",
|
|
93
|
+
name: jsxIdentifier.name,
|
|
94
|
+
// TODO: fix <_components["hypened-name"]></>
|
|
95
|
+
// name: containsHyphen(jsxIdentifier.name)
|
|
96
|
+
// ? '"' + jsxIdentifier.name + '"'
|
|
97
|
+
// : jsxIdentifier.name,
|
|
98
|
+
// computed: containsHyphen(jsxIdentifier.name) // proposal to "estree-jsx" for JSXMemberExpression
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
if (!componentMap[jsxIdentifier.name]) {
|
|
102
|
+
componentMap[jsxIdentifier.name] = jsxIdentifier.name;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return CONTINUE;
|
|
107
|
+
});
|
|
108
|
+
if (!Object.keys(componentMap).length)
|
|
109
|
+
return;
|
|
110
|
+
// find "const _components = {}" variable declarator; and add the components inside, if not exist.
|
|
111
|
+
visit(functionNode, (node) => {
|
|
112
|
+
if (node.type !== "VariableDeclarator")
|
|
113
|
+
return CONTINUE;
|
|
114
|
+
if (node.id.type === "Identifier" && node.id.name === "_components") {
|
|
115
|
+
targetVariableDeclarator = node;
|
|
116
|
+
if (node.init?.type === "ObjectExpression") {
|
|
117
|
+
const properties = node.init.properties;
|
|
118
|
+
const existingComponentMap = {};
|
|
119
|
+
for (const property of properties) {
|
|
120
|
+
if (property.type === "Property") {
|
|
121
|
+
if (property.key.type === "Identifier" &&
|
|
122
|
+
property.value.type === "Literal" &&
|
|
123
|
+
typeof property.value.value === "string") {
|
|
124
|
+
existingComponentMap[property.key.name] = property.value.value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const diffComponentMap = Object.entries(componentMap).filter(([key]) => !existingComponentMap[key]);
|
|
129
|
+
if (diffComponentMap.length) {
|
|
130
|
+
node.init.properties.splice(node.init.properties.length - 1, 0, ...diffComponentMap.map(([key, value]) => ({
|
|
131
|
+
type: "Property",
|
|
132
|
+
kind: "init",
|
|
133
|
+
key: containsHyphen(key)
|
|
134
|
+
? { type: "Literal", value: key }
|
|
135
|
+
: { type: "Identifier", name: key },
|
|
136
|
+
value: { type: "Literal", value },
|
|
137
|
+
method: false,
|
|
138
|
+
shorthand: false,
|
|
139
|
+
computed: false,
|
|
140
|
+
})));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return CONTINUE;
|
|
145
|
+
});
|
|
146
|
+
if (targetVariableDeclarator)
|
|
147
|
+
return;
|
|
148
|
+
// There is no "_components" declarator; so we will add VariableDeclaration ourself
|
|
149
|
+
functionNode.body.body.unshift({
|
|
150
|
+
type: "VariableDeclaration",
|
|
151
|
+
kind: "const",
|
|
152
|
+
declarations: [
|
|
153
|
+
{
|
|
154
|
+
type: "VariableDeclarator",
|
|
155
|
+
id: { type: "Identifier", name: "_components" },
|
|
156
|
+
init: {
|
|
157
|
+
type: "ObjectExpression",
|
|
158
|
+
properties: [
|
|
159
|
+
...Object.entries(componentMap).map(([key, value]) => ({
|
|
160
|
+
type: "Property",
|
|
161
|
+
kind: "init",
|
|
162
|
+
key: { type: "Identifier", name: key },
|
|
163
|
+
value: { type: "Literal", value },
|
|
164
|
+
method: false,
|
|
165
|
+
shorthand: false,
|
|
166
|
+
computed: false,
|
|
167
|
+
})),
|
|
168
|
+
{
|
|
169
|
+
type: "SpreadElement",
|
|
170
|
+
argument: {
|
|
171
|
+
type: "MemberExpression",
|
|
172
|
+
object: { type: "Identifier", name: functionPropsName },
|
|
173
|
+
property: { type: "Identifier", name: "components" },
|
|
174
|
+
computed: false,
|
|
175
|
+
optional: false,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
export default plugin;
|
|
186
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../../src/index.ts"],"version":"5.8.2"}
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "recma-mdx-html-override",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Recma plugin to ensure selected html raw elements overridable via mdx components in MDX.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": "./dist/esm/index.js",
|
|
7
|
+
"main": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/esm/index.d.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "rimraf dist && tsc --build && type-coverage",
|
|
11
|
+
"format": "npm run prettier && npm run lint",
|
|
12
|
+
"prettier": "prettier --write .",
|
|
13
|
+
"lint": "eslint .",
|
|
14
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --config ./jest.config.cjs --coverage",
|
|
15
|
+
"test:file": "NODE_OPTIONS=--experimental-vm-modules jest --config ./jest.config.cjs test1.jsx.spec.ts",
|
|
16
|
+
"prepack": "npm run build",
|
|
17
|
+
"prepublishOnly": "npm test && npm run format"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/",
|
|
21
|
+
"src/",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/ipikuka/recma-mdx-html-override.git"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"unified",
|
|
31
|
+
"estree",
|
|
32
|
+
"esast",
|
|
33
|
+
"mdx",
|
|
34
|
+
"mdxjs",
|
|
35
|
+
"plugin",
|
|
36
|
+
"recma",
|
|
37
|
+
"recma plugin",
|
|
38
|
+
"recma mdx",
|
|
39
|
+
"html override",
|
|
40
|
+
"html raw",
|
|
41
|
+
"recma mdx html override"
|
|
42
|
+
],
|
|
43
|
+
"author": "ipikuka <talatkuyuk@gmail.com>",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"homepage": "https://github.com/ipikuka/recma-mdx-html-override#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/ipikuka/recma-mdx-html-override/issues"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@eslint/js": "^9.20.0",
|
|
51
|
+
"@mdx-js/mdx": "^3.1.0",
|
|
52
|
+
"@types/dedent": "^0.7.2",
|
|
53
|
+
"@types/jest": "^29.5.14",
|
|
54
|
+
"@types/node": "^22.13.9",
|
|
55
|
+
"dedent": "^1.5.3",
|
|
56
|
+
"eslint": "^9.21.0",
|
|
57
|
+
"eslint-config-prettier": "^10.0.2",
|
|
58
|
+
"eslint-plugin-jest": "^28.11.0",
|
|
59
|
+
"eslint-plugin-prettier": "^5.2.3",
|
|
60
|
+
"jest": "^29.7.0",
|
|
61
|
+
"prettier": "^3.5.3",
|
|
62
|
+
"prettier-2": "npm:prettier@^2.8.8",
|
|
63
|
+
"rimraf": "^5.0.10",
|
|
64
|
+
"ts-jest": "^29.2.6",
|
|
65
|
+
"type-coverage": "^2.29.7",
|
|
66
|
+
"typescript": "^5.8.2",
|
|
67
|
+
"typescript-eslint": "^8.26.0",
|
|
68
|
+
"unified": "^11.0.5"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@types/estree": "^1.0.6",
|
|
72
|
+
"@types/estree-jsx": "^1.0.5",
|
|
73
|
+
"estree-util-visit": "^2.0.0"
|
|
74
|
+
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"unified": "^11"
|
|
77
|
+
},
|
|
78
|
+
"sideEffects": false,
|
|
79
|
+
"typeCoverage": {
|
|
80
|
+
"atLeast": 100,
|
|
81
|
+
"detail": true,
|
|
82
|
+
"ignoreAsAssertion": true,
|
|
83
|
+
"strict": true
|
|
84
|
+
}
|
|
85
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type { Plugin } from "unified";
|
|
2
|
+
import type { FunctionDeclaration, Node, Program, Property, VariableDeclarator } from "estree";
|
|
3
|
+
import type { JSXIdentifier, JSXOpeningElement } from "estree-jsx";
|
|
4
|
+
import { CONTINUE, EXIT, SKIP, visit } from "estree-util-visit";
|
|
5
|
+
|
|
6
|
+
export type HtmlOverrideOptions = {
|
|
7
|
+
tags?: string | string[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const DEFAULT_SETTINGS: HtmlOverrideOptions = {
|
|
11
|
+
tags: undefined,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* It is a recma plugin which makes selected html raw elements overridable.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
const plugin: Plugin<[HtmlOverrideOptions?], Program> = (options = {}) => {
|
|
20
|
+
const settings = Object.assign(
|
|
21
|
+
{},
|
|
22
|
+
DEFAULT_SETTINGS,
|
|
23
|
+
options,
|
|
24
|
+
) as Required<HtmlOverrideOptions>;
|
|
25
|
+
|
|
26
|
+
const componentMap: Record<string, string> = {};
|
|
27
|
+
let functionNode: FunctionDeclaration | undefined;
|
|
28
|
+
let functionPropsName: string = "props";
|
|
29
|
+
let targetVariableDeclarator: VariableDeclarator;
|
|
30
|
+
|
|
31
|
+
function containsHyphen(name: string): boolean {
|
|
32
|
+
return name.includes("-");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (tree: Node) => {
|
|
36
|
+
// console.dir(tree, { depth: 16 });
|
|
37
|
+
if (!settings.tags) return;
|
|
38
|
+
|
|
39
|
+
// finds the function _createMdxContent(){}
|
|
40
|
+
visit(tree, (node, _, index) => {
|
|
41
|
+
if (index === undefined) return;
|
|
42
|
+
|
|
43
|
+
if (node.type !== "FunctionDeclaration") return SKIP;
|
|
44
|
+
|
|
45
|
+
if (node.id.name === "_createMdxContent") {
|
|
46
|
+
functionNode = node;
|
|
47
|
+
|
|
48
|
+
const param = node.params[0];
|
|
49
|
+
if (param.type === "Identifier") {
|
|
50
|
+
functionPropsName = param.name;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return EXIT;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* istanbul ignore next */
|
|
57
|
+
return CONTINUE;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/* istanbul ignore next */
|
|
61
|
+
if (!functionNode) return;
|
|
62
|
+
|
|
63
|
+
// trace call expressions to change _jsx("xxx", {}) to _jsx(_components.xxx, {})
|
|
64
|
+
visit(functionNode, (node) => {
|
|
65
|
+
if (node.type !== "CallExpression") return CONTINUE;
|
|
66
|
+
|
|
67
|
+
if ("name" in node.callee) {
|
|
68
|
+
if (
|
|
69
|
+
node.callee.name !== "_jsx" &&
|
|
70
|
+
node.callee.name !== "_jsxDEV" &&
|
|
71
|
+
node.callee.name !== "_jsxs"
|
|
72
|
+
) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// First child of a CallExpression is a Literal or Identifier to a reference
|
|
78
|
+
const firstArgument = node.arguments[0];
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
firstArgument.type === "Literal" &&
|
|
82
|
+
typeof firstArgument.value === "string" &&
|
|
83
|
+
((typeof settings.tags === "string" && firstArgument.value === settings.tags) ||
|
|
84
|
+
settings.tags.includes(firstArgument.value))
|
|
85
|
+
) {
|
|
86
|
+
node.arguments[0] = {
|
|
87
|
+
type: "MemberExpression",
|
|
88
|
+
object: { type: "Identifier", name: "_components" },
|
|
89
|
+
property: {
|
|
90
|
+
type: "Identifier",
|
|
91
|
+
name: containsHyphen(firstArgument.value)
|
|
92
|
+
? '"' + firstArgument.value + '"'
|
|
93
|
+
: firstArgument.value,
|
|
94
|
+
},
|
|
95
|
+
computed: containsHyphen(firstArgument.value),
|
|
96
|
+
optional: false,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
if (!componentMap[firstArgument.value]) {
|
|
100
|
+
componentMap[firstArgument.value] = firstArgument.value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return CONTINUE;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// trace jsx elements to change <xxx /> to <__components.xxx />
|
|
108
|
+
visit(functionNode, (node) => {
|
|
109
|
+
if (node.type !== "JSXElement") return CONTINUE;
|
|
110
|
+
|
|
111
|
+
// First child of a CallExpression is a Literal or Identifier to a reference
|
|
112
|
+
const openingElement: JSXOpeningElement = node.openingElement;
|
|
113
|
+
|
|
114
|
+
if (openingElement.name.type === "JSXIdentifier") {
|
|
115
|
+
const jsxIdentifier: JSXIdentifier = openingElement.name;
|
|
116
|
+
|
|
117
|
+
if (
|
|
118
|
+
(typeof settings.tags === "string" && jsxIdentifier.name === settings.tags) ||
|
|
119
|
+
settings.tags.includes(jsxIdentifier.name)
|
|
120
|
+
) {
|
|
121
|
+
node.openingElement.name = {
|
|
122
|
+
type: "JSXMemberExpression",
|
|
123
|
+
object: { type: "JSXIdentifier", name: "_components" },
|
|
124
|
+
property: {
|
|
125
|
+
type: "JSXIdentifier",
|
|
126
|
+
name: jsxIdentifier.name,
|
|
127
|
+
// TODO: fix <_components["hypened-name"]></>
|
|
128
|
+
// name: containsHyphen(jsxIdentifier.name)
|
|
129
|
+
// ? '"' + jsxIdentifier.name + '"'
|
|
130
|
+
// : jsxIdentifier.name,
|
|
131
|
+
// computed: containsHyphen(jsxIdentifier.name) // proposal to "estree-jsx" for JSXMemberExpression
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (!componentMap[jsxIdentifier.name]) {
|
|
136
|
+
componentMap[jsxIdentifier.name] = jsxIdentifier.name;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return CONTINUE;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!Object.keys(componentMap).length) return;
|
|
145
|
+
|
|
146
|
+
// find "const _components = {}" variable declarator; and add the components inside, if not exist.
|
|
147
|
+
visit(functionNode, (node) => {
|
|
148
|
+
if (node.type !== "VariableDeclarator") return CONTINUE;
|
|
149
|
+
|
|
150
|
+
if (node.id.type === "Identifier" && node.id.name === "_components") {
|
|
151
|
+
targetVariableDeclarator = node;
|
|
152
|
+
|
|
153
|
+
if (node.init?.type === "ObjectExpression") {
|
|
154
|
+
const properties = node.init.properties;
|
|
155
|
+
|
|
156
|
+
const existingComponentMap: Record<string, string> = {};
|
|
157
|
+
|
|
158
|
+
for (const property of properties) {
|
|
159
|
+
if (property.type === "Property") {
|
|
160
|
+
if (
|
|
161
|
+
property.key.type === "Identifier" &&
|
|
162
|
+
property.value.type === "Literal" &&
|
|
163
|
+
typeof property.value.value === "string"
|
|
164
|
+
) {
|
|
165
|
+
existingComponentMap[property.key.name] = property.value.value;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const diffComponentMap = Object.entries(componentMap).filter(
|
|
171
|
+
([key]) => !existingComponentMap[key],
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
if (diffComponentMap.length) {
|
|
175
|
+
node.init.properties.splice(
|
|
176
|
+
node.init.properties.length - 1,
|
|
177
|
+
0,
|
|
178
|
+
...diffComponentMap.map(
|
|
179
|
+
([key, value]) =>
|
|
180
|
+
({
|
|
181
|
+
type: "Property",
|
|
182
|
+
kind: "init",
|
|
183
|
+
key: containsHyphen(key)
|
|
184
|
+
? { type: "Literal", value: key }
|
|
185
|
+
: { type: "Identifier", name: key },
|
|
186
|
+
value: { type: "Literal", value },
|
|
187
|
+
method: false,
|
|
188
|
+
shorthand: false,
|
|
189
|
+
computed: false,
|
|
190
|
+
}) as Property,
|
|
191
|
+
),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return CONTINUE;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (targetVariableDeclarator) return;
|
|
201
|
+
|
|
202
|
+
// There is no "_components" declarator; so we will add VariableDeclaration ourself
|
|
203
|
+
functionNode.body.body.unshift({
|
|
204
|
+
type: "VariableDeclaration",
|
|
205
|
+
kind: "const",
|
|
206
|
+
declarations: [
|
|
207
|
+
{
|
|
208
|
+
type: "VariableDeclarator",
|
|
209
|
+
id: { type: "Identifier", name: "_components" },
|
|
210
|
+
init: {
|
|
211
|
+
type: "ObjectExpression",
|
|
212
|
+
properties: [
|
|
213
|
+
...Object.entries(componentMap).map(
|
|
214
|
+
([key, value]) =>
|
|
215
|
+
({
|
|
216
|
+
type: "Property",
|
|
217
|
+
kind: "init",
|
|
218
|
+
key: { type: "Identifier", name: key },
|
|
219
|
+
value: { type: "Literal", value },
|
|
220
|
+
method: false,
|
|
221
|
+
shorthand: false,
|
|
222
|
+
computed: false,
|
|
223
|
+
}) as Property,
|
|
224
|
+
),
|
|
225
|
+
{
|
|
226
|
+
type: "SpreadElement",
|
|
227
|
+
argument: {
|
|
228
|
+
type: "MemberExpression",
|
|
229
|
+
object: { type: "Identifier", name: functionPropsName },
|
|
230
|
+
property: { type: "Identifier", name: "components" },
|
|
231
|
+
computed: false,
|
|
232
|
+
optional: false,
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export default plugin;
|