@readme/markdown 15.7.2 → 15.8.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/README.md +2 -2
- package/components/Callout/style.scss +15 -0
- package/dist/lib/compile.d.ts +8 -1
- package/dist/lib/mdxish.d.ts +7 -0
- package/dist/main.css +1 -1
- package/dist/main.css.map +1 -1
- package/dist/main.js +159 -7
- package/dist/main.node.js +159 -7
- package/dist/main.node.js.map +1 -1
- package/dist/processor/plugin/strip-tags.d.ts +31 -0
- package/dist/processor/transform/mdxish/evaluate-exports.d.ts +5 -1
- package/dist/render-fixture.node.js +159 -7
- package/dist/render-fixture.node.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Program } from 'estree';
|
|
2
|
+
import type { Root } from 'hast';
|
|
3
|
+
import type { Plugin, Transformer } from 'unified';
|
|
4
|
+
/**
|
|
5
|
+
* Tags removed from user-provided content because rendering them executes in the
|
|
6
|
+
* page. Add an entry to cover another vector; nothing else needs to change.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately narrow for now — `<script>` is the only vector this plugin claims
|
|
9
|
+
* to close. Broader parity with the `md` sanitization schema (`javascript:` URLs,
|
|
10
|
+
* `on*` handlers, `<iframe>`, `<object>`, …) is tracked in readmeio/markdown#1526.
|
|
11
|
+
*/
|
|
12
|
+
export declare const STRIPPED_TAG_NAMES: Set<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Removes matching elements and their subtrees when `sanitize` is enabled.
|
|
15
|
+
*
|
|
16
|
+
* Explicit `HTMLBlock`s are an intentional exception, matching legacy: their
|
|
17
|
+
* scripts live in a string prop rather than as elements, so they never appear
|
|
18
|
+
* in SSR HTML, and they only execute client-side when the author opts in via
|
|
19
|
+
* `runScripts`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const rehypeStripTags: () => Transformer<Root, Root>;
|
|
22
|
+
/**
|
|
23
|
+
* JSX inside expressions and exports stays in ESTree, outside the HAST pass.
|
|
24
|
+
* Strip it before evaluation in mdxish or after JSX compilation in MDX.
|
|
25
|
+
*
|
|
26
|
+
* Elements are spliced out of JSX children and replaced with `null` anywhere
|
|
27
|
+
* else, which React renders as nothing.
|
|
28
|
+
*/
|
|
29
|
+
export declare const stripJsxTags: (program: Program) => Program;
|
|
30
|
+
/** `stripJsxTags` as a recma plugin, for the `mdx` compile pipeline. */
|
|
31
|
+
export declare const recmaStripTags: Plugin<[], Program>;
|
|
@@ -11,6 +11,10 @@ declare module 'hast' {
|
|
|
11
11
|
mdxishScope?: MdxishScope;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
interface Options {
|
|
15
|
+
/** Drop `STRIPPED_TAG_NAMES` JSX from the exported code before it is evaluated. */
|
|
16
|
+
sanitize?: boolean;
|
|
17
|
+
}
|
|
14
18
|
/**
|
|
15
19
|
* Evaluate `export const/function` declarations introduced by mdxjsEsm nodes.
|
|
16
20
|
*
|
|
@@ -23,5 +27,5 @@ declare module 'hast' {
|
|
|
23
27
|
* Any evaluation error is consumed and logged. We don't throw because it's
|
|
24
28
|
* against the spirit of this engine to be less permissive than MDX needs.
|
|
25
29
|
*/
|
|
26
|
-
declare const evaluateExports: Plugin<[], Root>;
|
|
30
|
+
declare const evaluateExports: Plugin<[Options?], Root>;
|
|
27
31
|
export default evaluateExports;
|
|
@@ -19045,7 +19045,7 @@ module.exports = function () {
|
|
|
19045
19045
|
|
|
19046
19046
|
/***/ },
|
|
19047
19047
|
|
|
19048
|
-
/***/
|
|
19048
|
+
/***/ 6829
|
|
19049
19049
|
(module, __webpack_exports__, __webpack_require__) {
|
|
19050
19050
|
|
|
19051
19051
|
"use strict";
|
|
@@ -114138,6 +114138,133 @@ const hardBreaks = () => tree => {
|
|
|
114138
114138
|
};
|
|
114139
114139
|
/* harmony default export */ const hard_breaks = (hardBreaks);
|
|
114140
114140
|
|
|
114141
|
+
;// ./processor/plugin/strip-tags.ts
|
|
114142
|
+
|
|
114143
|
+
/**
|
|
114144
|
+
* Tags removed from user-provided content because rendering them executes in the
|
|
114145
|
+
* page. Add an entry to cover another vector; nothing else needs to change.
|
|
114146
|
+
*
|
|
114147
|
+
* Deliberately narrow for now — `<script>` is the only vector this plugin claims
|
|
114148
|
+
* to close. Broader parity with the `md` sanitization schema (`javascript:` URLs,
|
|
114149
|
+
* `on*` handlers, `<iframe>`, `<object>`, …) is tracked in readmeio/markdown#1526.
|
|
114150
|
+
*/
|
|
114151
|
+
const STRIPPED_TAG_NAMES = new Set(['script']);
|
|
114152
|
+
/** JSX names starting with a capital are component references, not literal tags. */
|
|
114153
|
+
const LITERAL_TAG_NAME = /^[a-z]/;
|
|
114154
|
+
/**
|
|
114155
|
+
* Whether a node would render as one of the stripped literal DOM elements.
|
|
114156
|
+
* Browsers match tag names case-insensitively, so `<sCrIpT>` executes too.
|
|
114157
|
+
*
|
|
114158
|
+
* `<Script />` and friends are component references and are left alone.
|
|
114159
|
+
*/
|
|
114160
|
+
const isStrippedTag = (node) => {
|
|
114161
|
+
if (node.type === 'element')
|
|
114162
|
+
return STRIPPED_TAG_NAMES.has(node.tagName.toLowerCase());
|
|
114163
|
+
if (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') {
|
|
114164
|
+
const name = node.name ?? '';
|
|
114165
|
+
return LITERAL_TAG_NAME.test(name) && STRIPPED_TAG_NAMES.has(name.toLowerCase());
|
|
114166
|
+
}
|
|
114167
|
+
return false;
|
|
114168
|
+
};
|
|
114169
|
+
/**
|
|
114170
|
+
* Removes matching elements and their subtrees when `sanitize` is enabled.
|
|
114171
|
+
*
|
|
114172
|
+
* Explicit `HTMLBlock`s are an intentional exception, matching legacy: their
|
|
114173
|
+
* scripts live in a string prop rather than as elements, so they never appear
|
|
114174
|
+
* in SSR HTML, and they only execute client-side when the author opts in via
|
|
114175
|
+
* `runScripts`.
|
|
114176
|
+
*/
|
|
114177
|
+
const rehypeStripTags = () => {
|
|
114178
|
+
return (tree) => {
|
|
114179
|
+
visit(tree, isStrippedTag, (_node, index, parent) => {
|
|
114180
|
+
parent.children.splice(index, 1);
|
|
114181
|
+
return [(/* inlined export .SKIP */"skip"), index];
|
|
114182
|
+
});
|
|
114183
|
+
};
|
|
114184
|
+
};
|
|
114185
|
+
/** The automatic-runtime factories MDX compiles JSX into. */
|
|
114186
|
+
const JSX_FACTORIES = new Set(['_jsx', '_jsxs', '_jsxDEV']);
|
|
114187
|
+
/**
|
|
114188
|
+
* Matches raw JSX and compiled `_jsx('tag', …)` calls. MDX may rewrite literal
|
|
114189
|
+
* tags as `_components.<tag>` so a `components` prop can override them.
|
|
114190
|
+
*/
|
|
114191
|
+
const isStrippedJsxElement = (node) => {
|
|
114192
|
+
if (node.type === 'JSXElement') {
|
|
114193
|
+
const { name } = node.openingElement;
|
|
114194
|
+
if (name.type === 'JSXIdentifier') {
|
|
114195
|
+
return LITERAL_TAG_NAME.test(name.name) && STRIPPED_TAG_NAMES.has(name.name.toLowerCase());
|
|
114196
|
+
}
|
|
114197
|
+
return (name.type === 'JSXMemberExpression' &&
|
|
114198
|
+
name.object.type === 'JSXIdentifier' &&
|
|
114199
|
+
name.object.name === '_components' &&
|
|
114200
|
+
STRIPPED_TAG_NAMES.has(name.property.name.toLowerCase()));
|
|
114201
|
+
}
|
|
114202
|
+
if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && JSX_FACTORIES.has(node.callee.name)) {
|
|
114203
|
+
const [tag] = node.arguments;
|
|
114204
|
+
if (!tag)
|
|
114205
|
+
return false;
|
|
114206
|
+
if (tag.type === 'Literal')
|
|
114207
|
+
return typeof tag.value === 'string' && STRIPPED_TAG_NAMES.has(tag.value.toLowerCase());
|
|
114208
|
+
return (tag.type === 'MemberExpression' &&
|
|
114209
|
+
tag.object.type === 'Identifier' &&
|
|
114210
|
+
tag.object.name === '_components' &&
|
|
114211
|
+
tag.property.type === 'Identifier' &&
|
|
114212
|
+
STRIPPED_TAG_NAMES.has(tag.property.name.toLowerCase()));
|
|
114213
|
+
}
|
|
114214
|
+
return false;
|
|
114215
|
+
};
|
|
114216
|
+
const isEstreeNode = (value) => typeof value === 'object' && value !== null && typeof value.type === 'string';
|
|
114217
|
+
const nullLiteral = () => ({ type: 'Literal', value: null });
|
|
114218
|
+
/**
|
|
114219
|
+
* JSX inside expressions and exports stays in ESTree, outside the HAST pass.
|
|
114220
|
+
* Strip it before evaluation in mdxish or after JSX compilation in MDX.
|
|
114221
|
+
*
|
|
114222
|
+
* Elements are spliced out of JSX children and replaced with `null` anywhere
|
|
114223
|
+
* else, which React renders as nothing.
|
|
114224
|
+
*/
|
|
114225
|
+
const stripJsxTags = (program) => {
|
|
114226
|
+
const walk = (node) => {
|
|
114227
|
+
const record = node;
|
|
114228
|
+
Object.keys(record).forEach(key => {
|
|
114229
|
+
if (key === 'loc' || key === 'range' || key === 'position')
|
|
114230
|
+
return;
|
|
114231
|
+
const value = record[key];
|
|
114232
|
+
if (Array.isArray(value)) {
|
|
114233
|
+
for (let i = value.length - 1; i >= 0; i -= 1) {
|
|
114234
|
+
const child = value[i];
|
|
114235
|
+
if (isEstreeNode(child)) {
|
|
114236
|
+
if (isStrippedJsxElement(child)) {
|
|
114237
|
+
if (key === 'children')
|
|
114238
|
+
value.splice(i, 1);
|
|
114239
|
+
else
|
|
114240
|
+
value[i] = nullLiteral();
|
|
114241
|
+
}
|
|
114242
|
+
else {
|
|
114243
|
+
walk(child);
|
|
114244
|
+
}
|
|
114245
|
+
}
|
|
114246
|
+
}
|
|
114247
|
+
return;
|
|
114248
|
+
}
|
|
114249
|
+
if (!isEstreeNode(value))
|
|
114250
|
+
return;
|
|
114251
|
+
if (isStrippedJsxElement(value)) {
|
|
114252
|
+
record[key] =
|
|
114253
|
+
node.type === 'JSXAttribute' ? { type: 'JSXExpressionContainer', expression: nullLiteral() } : nullLiteral();
|
|
114254
|
+
}
|
|
114255
|
+
else {
|
|
114256
|
+
walk(value);
|
|
114257
|
+
}
|
|
114258
|
+
});
|
|
114259
|
+
};
|
|
114260
|
+
walk(program);
|
|
114261
|
+
return program;
|
|
114262
|
+
};
|
|
114263
|
+
/** `stripJsxTags` as a recma plugin, for the `mdx` compile pipeline. */
|
|
114264
|
+
const recmaStripTags = () => tree => {
|
|
114265
|
+
stripJsxTags(tree);
|
|
114266
|
+
};
|
|
114267
|
+
|
|
114141
114268
|
;// ./node_modules/estree-util-value-to-estree/dist/estree-util-value-to-estree.js
|
|
114142
114269
|
/**
|
|
114143
114270
|
* Create an ESTree identifier node for a given name.
|
|
@@ -116875,6 +117002,7 @@ const tocHastToMdx = (toc, components, variables) => {
|
|
|
116875
117002
|
|
|
116876
117003
|
|
|
116877
117004
|
|
|
117005
|
+
|
|
116878
117006
|
const sanitizeSchema = cjs_default()(defaultSchema, {
|
|
116879
117007
|
attributes: {
|
|
116880
117008
|
a: ['target'],
|
|
@@ -116883,7 +117011,10 @@ const sanitizeSchema = cjs_default()(defaultSchema, {
|
|
|
116883
117011
|
href: ['doc', 'ref', 'blog', 'changelog', 'page'],
|
|
116884
117012
|
},
|
|
116885
117013
|
});
|
|
116886
|
-
const compile_compile = (text, { components = {}, missingComponents, copyButtons, useTailwind, hardBreaks,
|
|
117014
|
+
const compile_compile = (text, { components = {}, missingComponents, copyButtons, useTailwind, hardBreaks, sanitize = false,
|
|
117015
|
+
// Pulled out of `...opts` so a caller's plugins extend the pipeline instead of
|
|
117016
|
+
// replacing it — otherwise the spread below would drop `rehypeStripTags`.
|
|
117017
|
+
remarkPlugins: userRemarkPlugins, rehypePlugins: userRehypePlugins, recmaPlugins: userRecmaPlugins, ...opts } = {}) => {
|
|
116887
117018
|
// Destructure at runtime to avoid circular dependency issues
|
|
116888
117019
|
const { codeTabsTransformer, ...transforms } = defaultTransforms;
|
|
116889
117020
|
const remarkPlugins = [
|
|
@@ -116901,7 +117032,12 @@ const compile_compile = (text, { components = {}, missingComponents, copyButtons
|
|
|
116901
117032
|
if (useTailwind) {
|
|
116902
117033
|
remarkPlugins.push([transform_tailwind, { components }]);
|
|
116903
117034
|
}
|
|
116904
|
-
|
|
117035
|
+
remarkPlugins.push(...(userRemarkPlugins ?? []));
|
|
117036
|
+
const rehypePlugins = [
|
|
117037
|
+
...ast_processor_rehypePlugins,
|
|
117038
|
+
[rehypeToc, { components }],
|
|
117039
|
+
...(userRehypePlugins ?? []),
|
|
117040
|
+
];
|
|
116905
117041
|
if (opts.format === 'md') {
|
|
116906
117042
|
/**
|
|
116907
117043
|
* When running in `md` format we need to sanitize the content for security reasons.
|
|
@@ -116919,12 +117055,23 @@ const compile_compile = (text, { components = {}, missingComponents, copyButtons
|
|
|
116919
117055
|
]);
|
|
116920
117056
|
rehypePlugins.push([rehypeSanitize, sanitizeSchema]);
|
|
116921
117057
|
}
|
|
117058
|
+
const recmaPlugins = [...(userRecmaPlugins ?? [])];
|
|
117059
|
+
// In `mdx` format a literal <script> parses as JSX rather than raw HTML, so it
|
|
117060
|
+
// bypasses sanitization entirely — strip it in every format. Must stay last:
|
|
117061
|
+
// in `md` format raw HTML isn't an element until `rehypeRaw` has run. A tag
|
|
117062
|
+
// inside an expression or export is code, not an element, so the recma pass
|
|
117063
|
+
// catches it in the compiled program instead.
|
|
117064
|
+
if (sanitize) {
|
|
117065
|
+
rehypePlugins.push(rehypeStripTags);
|
|
117066
|
+
recmaPlugins.push(recmaStripTags);
|
|
117067
|
+
}
|
|
116922
117068
|
try {
|
|
116923
117069
|
const vfile = compileSync(text, {
|
|
116924
117070
|
outputFormat: 'function-body',
|
|
116925
117071
|
providerImportSource: '#',
|
|
116926
117072
|
remarkPlugins,
|
|
116927
117073
|
rehypePlugins,
|
|
117074
|
+
recmaPlugins,
|
|
116928
117075
|
...opts,
|
|
116929
117076
|
});
|
|
116930
117077
|
const string = String(vfile)
|
|
@@ -123598,6 +123745,7 @@ const collectImportValues = (importDeclarations, registry = defaultModuleRegistr
|
|
|
123598
123745
|
|
|
123599
123746
|
|
|
123600
123747
|
|
|
123748
|
+
|
|
123601
123749
|
/**
|
|
123602
123750
|
* Recursively extract all identifier names introduced by a binding pattern.
|
|
123603
123751
|
* Handles destructuring (`{ a, b }`, `[x, y]`), rest elements (`...rest`),
|
|
@@ -123642,7 +123790,7 @@ const collectExportNames = (declaration) => {
|
|
|
123642
123790
|
* Any evaluation error is consumed and logged. We don't throw because it's
|
|
123643
123791
|
* against the spirit of this engine to be less permissive than MDX needs.
|
|
123644
123792
|
*/
|
|
123645
|
-
const evaluateExports = () => (tree, file) => {
|
|
123793
|
+
const evaluateExports = (opts) => (tree, file) => {
|
|
123646
123794
|
const programBody = [];
|
|
123647
123795
|
const exportNames = [];
|
|
123648
123796
|
const importDeclarations = [];
|
|
@@ -123688,6 +123836,8 @@ const evaluateExports = () => (tree, file) => {
|
|
|
123688
123836
|
// Evaluate the declarations together at once in a single sandboxed Function
|
|
123689
123837
|
try {
|
|
123690
123838
|
const program = { type: 'Program', sourceType: 'module', body: programBody };
|
|
123839
|
+
if (opts?.sanitize)
|
|
123840
|
+
stripJsxTags(program);
|
|
123691
123841
|
buildJsx(program, { runtime: 'classic', pragma: 'React.createElement', pragmaFrag: 'React.Fragment' });
|
|
123692
123842
|
const { value: source } = toJs(program);
|
|
123693
123843
|
// Make sure import values are available in the scope
|
|
@@ -127884,6 +128034,7 @@ function loadComponents() {
|
|
|
127884
128034
|
|
|
127885
128035
|
|
|
127886
128036
|
|
|
128037
|
+
|
|
127887
128038
|
|
|
127888
128039
|
|
|
127889
128040
|
const defaultTransformers = [
|
|
@@ -128010,7 +128161,7 @@ function mdxishMdastToMd(mdast) {
|
|
|
128010
128161
|
* @see .claude/context/MDXish/Processor Overview.md
|
|
128011
128162
|
*/
|
|
128012
128163
|
function mdxish(mdContent, opts = {}) {
|
|
128013
|
-
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, safeMode = false, variables } = opts;
|
|
128164
|
+
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, safeMode = false, sanitize = false, variables, } = opts;
|
|
128014
128165
|
const components = {
|
|
128015
128166
|
...loadComponents(),
|
|
128016
128167
|
...userComponents,
|
|
@@ -128021,7 +128172,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
128021
128172
|
const contentWithoutComments = restoreCodeBlocks(withoutComments, protectedCode);
|
|
128022
128173
|
const { processor, parserReadyContent } = mdxishAstProcessor(contentWithoutComments, opts);
|
|
128023
128174
|
processor
|
|
128024
|
-
.use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
128175
|
+
.use(safeMode ? undefined : evaluate_exports, { sanitize }) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
128025
128176
|
.use(enableHardBreaks ? hard_breaks : undefined) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
|
|
128026
128177
|
.use(safeMode ? undefined : evaluate_expressions, { components, variables }) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
|
|
128027
128178
|
.use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
|
|
@@ -128032,6 +128183,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
128032
128183
|
.use(restoreBooleanProperties)
|
|
128033
128184
|
.use(safeMode ? undefined : resolve_deferred_attribute_expression_props) // Evaluate deferred attribute expressions on mdx-jsx nodes (now past rehypeRaw's clone)
|
|
128034
128185
|
.use(normalize_mdx_jsx_nodes) // Rewrite `mdx-jsx` back to standard `element` nodes for downstream plugins
|
|
128186
|
+
.use(sanitize ? rehypeStripTags : undefined) // Strip STRIPPED_TAG_NAMES elements; this pipeline has no sanitization step
|
|
128035
128187
|
.use(rehypeFlattenTableCellParagraphs) // Remove <p> wrappers inside table cells to prevent margin issues
|
|
128036
128188
|
.use(mdxish_mermaid) // Add mermaid-render className to pre wrappers
|
|
128037
128189
|
.use(heading_slugs)
|
|
@@ -129114,7 +129266,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
|
|
|
129114
129266
|
/******/ // startup
|
|
129115
129267
|
/******/ // Load entry module and return exports
|
|
129116
129268
|
/******/ // This entry module used 'module' so it can't be inlined
|
|
129117
|
-
/******/ let __webpack_exports__ = __webpack_require__(
|
|
129269
|
+
/******/ let __webpack_exports__ = __webpack_require__(6829);
|
|
129118
129270
|
/******/ module.exports = __webpack_exports__;
|
|
129119
129271
|
/******/
|
|
129120
129272
|
/******/ })()
|