@readme/markdown 15.7.3 → 15.9.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/components/Callout/style.scss +11 -2
- package/components/HTMLBlock/index.tsx +2 -2
- 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 +344 -32
- package/dist/main.node.js +344 -32
- package/dist/main.node.js.map +1 -1
- package/dist/processor/plugin/safe-mode-html-blocks.d.ts +12 -0
- package/dist/processor/plugin/strip-tags.d.ts +31 -0
- package/dist/processor/transform/mdxish/evaluate-exports.d.ts +5 -1
- package/dist/processor/transform/mdxish/magic-blocks/types.d.ts +0 -1
- package/dist/processor/transform/mdxish/tables/cell-style.d.ts +7 -0
- package/dist/processor/transform/mdxish/tables/cell-width.d.ts +9 -0
- package/dist/render-fixture.node.js +323 -32
- package/dist/render-fixture.node.js.map +1 -1
- package/package.json +2 -2
- package/types.d.ts +2 -0
package/dist/main.js
CHANGED
|
@@ -11423,7 +11423,7 @@ module.exports = function () {
|
|
|
11423
11423
|
|
|
11424
11424
|
/***/ },
|
|
11425
11425
|
|
|
11426
|
-
/***/
|
|
11426
|
+
/***/ 9171
|
|
11427
11427
|
(module, __webpack_exports__, __webpack_require__) {
|
|
11428
11428
|
|
|
11429
11429
|
"use strict";
|
|
@@ -12156,9 +12156,9 @@ const HTMLBlock = ({ children = '', html: htmlProp, runScripts, safeMode: safeMo
|
|
|
12156
12156
|
const safeMode = typeof safeModeRaw !== 'boolean' ? safeModeRaw === 'true' : safeModeRaw;
|
|
12157
12157
|
const [cleanedHtml, exec] = extractScripts(html);
|
|
12158
12158
|
(0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useEffect)(() => {
|
|
12159
|
-
if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts)
|
|
12159
|
+
if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts && !safeMode)
|
|
12160
12160
|
exec();
|
|
12161
|
-
}, [runScripts, exec]);
|
|
12161
|
+
}, [runScripts, safeMode, exec]);
|
|
12162
12162
|
if (nonStringChildren) {
|
|
12163
12163
|
// Fail soft: a non-string child (e.g. JSX that wasn't serialized back to a
|
|
12164
12164
|
// raw string) should never throw, so render the child nodes directly
|
|
@@ -94009,6 +94009,133 @@ const hardBreaks = () => tree => {
|
|
|
94009
94009
|
};
|
|
94010
94010
|
/* harmony default export */ const hard_breaks = (hardBreaks);
|
|
94011
94011
|
|
|
94012
|
+
;// ./processor/plugin/strip-tags.ts
|
|
94013
|
+
|
|
94014
|
+
/**
|
|
94015
|
+
* Tags removed from user-provided content because rendering them executes in the
|
|
94016
|
+
* page. Add an entry to cover another vector; nothing else needs to change.
|
|
94017
|
+
*
|
|
94018
|
+
* Deliberately narrow for now — `<script>` is the only vector this plugin claims
|
|
94019
|
+
* to close. Broader parity with the `md` sanitization schema (`javascript:` URLs,
|
|
94020
|
+
* `on*` handlers, `<iframe>`, `<object>`, …) is tracked in readmeio/markdown#1526.
|
|
94021
|
+
*/
|
|
94022
|
+
const STRIPPED_TAG_NAMES = new Set(['script']);
|
|
94023
|
+
/** JSX names starting with a capital are component references, not literal tags. */
|
|
94024
|
+
const LITERAL_TAG_NAME = /^[a-z]/;
|
|
94025
|
+
/**
|
|
94026
|
+
* Whether a node would render as one of the stripped literal DOM elements.
|
|
94027
|
+
* Browsers match tag names case-insensitively, so `<sCrIpT>` executes too.
|
|
94028
|
+
*
|
|
94029
|
+
* `<Script />` and friends are component references and are left alone.
|
|
94030
|
+
*/
|
|
94031
|
+
const isStrippedTag = (node) => {
|
|
94032
|
+
if (node.type === 'element')
|
|
94033
|
+
return STRIPPED_TAG_NAMES.has(node.tagName.toLowerCase());
|
|
94034
|
+
if (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') {
|
|
94035
|
+
const name = node.name ?? '';
|
|
94036
|
+
return LITERAL_TAG_NAME.test(name) && STRIPPED_TAG_NAMES.has(name.toLowerCase());
|
|
94037
|
+
}
|
|
94038
|
+
return false;
|
|
94039
|
+
};
|
|
94040
|
+
/**
|
|
94041
|
+
* Removes matching elements and their subtrees when `sanitize` is enabled.
|
|
94042
|
+
*
|
|
94043
|
+
* Explicit `HTMLBlock`s are an intentional exception, matching legacy: their
|
|
94044
|
+
* scripts live in a string prop rather than as elements, so they never appear
|
|
94045
|
+
* in SSR HTML, and they only execute client-side when the author opts in via
|
|
94046
|
+
* `runScripts`.
|
|
94047
|
+
*/
|
|
94048
|
+
const rehypeStripTags = () => {
|
|
94049
|
+
return (tree) => {
|
|
94050
|
+
visit(tree, isStrippedTag, (_node, index, parent) => {
|
|
94051
|
+
parent.children.splice(index, 1);
|
|
94052
|
+
return [(/* inlined export .SKIP */"skip"), index];
|
|
94053
|
+
});
|
|
94054
|
+
};
|
|
94055
|
+
};
|
|
94056
|
+
/** The automatic-runtime factories MDX compiles JSX into. */
|
|
94057
|
+
const JSX_FACTORIES = new Set(['_jsx', '_jsxs', '_jsxDEV']);
|
|
94058
|
+
/**
|
|
94059
|
+
* Matches raw JSX and compiled `_jsx('tag', …)` calls. MDX may rewrite literal
|
|
94060
|
+
* tags as `_components.<tag>` so a `components` prop can override them.
|
|
94061
|
+
*/
|
|
94062
|
+
const isStrippedJsxElement = (node) => {
|
|
94063
|
+
if (node.type === 'JSXElement') {
|
|
94064
|
+
const { name } = node.openingElement;
|
|
94065
|
+
if (name.type === 'JSXIdentifier') {
|
|
94066
|
+
return LITERAL_TAG_NAME.test(name.name) && STRIPPED_TAG_NAMES.has(name.name.toLowerCase());
|
|
94067
|
+
}
|
|
94068
|
+
return (name.type === 'JSXMemberExpression' &&
|
|
94069
|
+
name.object.type === 'JSXIdentifier' &&
|
|
94070
|
+
name.object.name === '_components' &&
|
|
94071
|
+
STRIPPED_TAG_NAMES.has(name.property.name.toLowerCase()));
|
|
94072
|
+
}
|
|
94073
|
+
if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && JSX_FACTORIES.has(node.callee.name)) {
|
|
94074
|
+
const [tag] = node.arguments;
|
|
94075
|
+
if (!tag)
|
|
94076
|
+
return false;
|
|
94077
|
+
if (tag.type === 'Literal')
|
|
94078
|
+
return typeof tag.value === 'string' && STRIPPED_TAG_NAMES.has(tag.value.toLowerCase());
|
|
94079
|
+
return (tag.type === 'MemberExpression' &&
|
|
94080
|
+
tag.object.type === 'Identifier' &&
|
|
94081
|
+
tag.object.name === '_components' &&
|
|
94082
|
+
tag.property.type === 'Identifier' &&
|
|
94083
|
+
STRIPPED_TAG_NAMES.has(tag.property.name.toLowerCase()));
|
|
94084
|
+
}
|
|
94085
|
+
return false;
|
|
94086
|
+
};
|
|
94087
|
+
const isEstreeNode = (value) => typeof value === 'object' && value !== null && typeof value.type === 'string';
|
|
94088
|
+
const nullLiteral = () => ({ type: 'Literal', value: null });
|
|
94089
|
+
/**
|
|
94090
|
+
* JSX inside expressions and exports stays in ESTree, outside the HAST pass.
|
|
94091
|
+
* Strip it before evaluation in mdxish or after JSX compilation in MDX.
|
|
94092
|
+
*
|
|
94093
|
+
* Elements are spliced out of JSX children and replaced with `null` anywhere
|
|
94094
|
+
* else, which React renders as nothing.
|
|
94095
|
+
*/
|
|
94096
|
+
const stripJsxTags = (program) => {
|
|
94097
|
+
const walk = (node) => {
|
|
94098
|
+
const record = node;
|
|
94099
|
+
Object.keys(record).forEach(key => {
|
|
94100
|
+
if (key === 'loc' || key === 'range' || key === 'position')
|
|
94101
|
+
return;
|
|
94102
|
+
const value = record[key];
|
|
94103
|
+
if (Array.isArray(value)) {
|
|
94104
|
+
for (let i = value.length - 1; i >= 0; i -= 1) {
|
|
94105
|
+
const child = value[i];
|
|
94106
|
+
if (isEstreeNode(child)) {
|
|
94107
|
+
if (isStrippedJsxElement(child)) {
|
|
94108
|
+
if (key === 'children')
|
|
94109
|
+
value.splice(i, 1);
|
|
94110
|
+
else
|
|
94111
|
+
value[i] = nullLiteral();
|
|
94112
|
+
}
|
|
94113
|
+
else {
|
|
94114
|
+
walk(child);
|
|
94115
|
+
}
|
|
94116
|
+
}
|
|
94117
|
+
}
|
|
94118
|
+
return;
|
|
94119
|
+
}
|
|
94120
|
+
if (!isEstreeNode(value))
|
|
94121
|
+
return;
|
|
94122
|
+
if (isStrippedJsxElement(value)) {
|
|
94123
|
+
record[key] =
|
|
94124
|
+
node.type === 'JSXAttribute' ? { type: 'JSXExpressionContainer', expression: nullLiteral() } : nullLiteral();
|
|
94125
|
+
}
|
|
94126
|
+
else {
|
|
94127
|
+
walk(value);
|
|
94128
|
+
}
|
|
94129
|
+
});
|
|
94130
|
+
};
|
|
94131
|
+
walk(program);
|
|
94132
|
+
return program;
|
|
94133
|
+
};
|
|
94134
|
+
/** `stripJsxTags` as a recma plugin, for the `mdx` compile pipeline. */
|
|
94135
|
+
const recmaStripTags = () => tree => {
|
|
94136
|
+
stripJsxTags(tree);
|
|
94137
|
+
};
|
|
94138
|
+
|
|
94012
94139
|
;// ./node_modules/estree-util-value-to-estree/dist/estree-util-value-to-estree.js
|
|
94013
94140
|
/**
|
|
94014
94141
|
* Create an ESTree identifier node for a given name.
|
|
@@ -96746,6 +96873,7 @@ const tocHastToMdx = (toc, components, variables) => {
|
|
|
96746
96873
|
|
|
96747
96874
|
|
|
96748
96875
|
|
|
96876
|
+
|
|
96749
96877
|
const sanitizeSchema = cjs_default()(defaultSchema, {
|
|
96750
96878
|
attributes: {
|
|
96751
96879
|
a: ['target'],
|
|
@@ -96754,7 +96882,10 @@ const sanitizeSchema = cjs_default()(defaultSchema, {
|
|
|
96754
96882
|
href: ['doc', 'ref', 'blog', 'changelog', 'page'],
|
|
96755
96883
|
},
|
|
96756
96884
|
});
|
|
96757
|
-
const compile_compile = (text, { components = {}, missingComponents, copyButtons, useTailwind, hardBreaks,
|
|
96885
|
+
const compile_compile = (text, { components = {}, missingComponents, copyButtons, useTailwind, hardBreaks, sanitize = false,
|
|
96886
|
+
// Pulled out of `...opts` so a caller's plugins extend the pipeline instead of
|
|
96887
|
+
// replacing it — otherwise the spread below would drop `rehypeStripTags`.
|
|
96888
|
+
remarkPlugins: userRemarkPlugins, rehypePlugins: userRehypePlugins, recmaPlugins: userRecmaPlugins, ...opts } = {}) => {
|
|
96758
96889
|
// Destructure at runtime to avoid circular dependency issues
|
|
96759
96890
|
const { codeTabsTransformer, ...transforms } = defaultTransforms;
|
|
96760
96891
|
const remarkPlugins = [
|
|
@@ -96772,7 +96903,12 @@ const compile_compile = (text, { components = {}, missingComponents, copyButtons
|
|
|
96772
96903
|
if (useTailwind) {
|
|
96773
96904
|
remarkPlugins.push([transform_tailwind, { components }]);
|
|
96774
96905
|
}
|
|
96775
|
-
|
|
96906
|
+
remarkPlugins.push(...(userRemarkPlugins ?? []));
|
|
96907
|
+
const rehypePlugins = [
|
|
96908
|
+
...ast_processor_rehypePlugins,
|
|
96909
|
+
[rehypeToc, { components }],
|
|
96910
|
+
...(userRehypePlugins ?? []),
|
|
96911
|
+
];
|
|
96776
96912
|
if (opts.format === 'md') {
|
|
96777
96913
|
/**
|
|
96778
96914
|
* When running in `md` format we need to sanitize the content for security reasons.
|
|
@@ -96790,12 +96926,23 @@ const compile_compile = (text, { components = {}, missingComponents, copyButtons
|
|
|
96790
96926
|
]);
|
|
96791
96927
|
rehypePlugins.push([rehypeSanitize, sanitizeSchema]);
|
|
96792
96928
|
}
|
|
96929
|
+
const recmaPlugins = [...(userRecmaPlugins ?? [])];
|
|
96930
|
+
// In `mdx` format a literal <script> parses as JSX rather than raw HTML, so it
|
|
96931
|
+
// bypasses sanitization entirely — strip it in every format. Must stay last:
|
|
96932
|
+
// in `md` format raw HTML isn't an element until `rehypeRaw` has run. A tag
|
|
96933
|
+
// inside an expression or export is code, not an element, so the recma pass
|
|
96934
|
+
// catches it in the compiled program instead.
|
|
96935
|
+
if (sanitize) {
|
|
96936
|
+
rehypePlugins.push(rehypeStripTags);
|
|
96937
|
+
recmaPlugins.push(recmaStripTags);
|
|
96938
|
+
}
|
|
96793
96939
|
try {
|
|
96794
96940
|
const vfile = compileSync(text, {
|
|
96795
96941
|
outputFormat: 'function-body',
|
|
96796
96942
|
providerImportSource: '#',
|
|
96797
96943
|
remarkPlugins,
|
|
96798
96944
|
rehypePlugins,
|
|
96945
|
+
recmaPlugins,
|
|
96799
96946
|
...opts,
|
|
96800
96947
|
});
|
|
96801
96948
|
const string = String(vfile)
|
|
@@ -103741,6 +103888,32 @@ const mdxComponentHandlers = {
|
|
|
103741
103888
|
[NodeTypes.htmlBlock]: htmlBlockHandler,
|
|
103742
103889
|
};
|
|
103743
103890
|
|
|
103891
|
+
;// ./processor/plugin/safe-mode-html-blocks.ts
|
|
103892
|
+
|
|
103893
|
+
|
|
103894
|
+
/**
|
|
103895
|
+
* Whether a tag name renders through the `HTMLBlock` component. Mirrors the
|
|
103896
|
+
* separator-agnostic, case-insensitive lookup in `getComponentName` and the
|
|
103897
|
+
* rehype-react component map, so `html-block`, `HTMLBlock`, `HtmlBlock` and
|
|
103898
|
+
* `htmlblock` all match.
|
|
103899
|
+
*/
|
|
103900
|
+
const isHtmlBlockTag = (tagName) => toPascalCase(tagName).toLowerCase() === 'htmlblock';
|
|
103901
|
+
/**
|
|
103902
|
+
* Forces `safeMode` on every element that renders through `HTMLBlock` so it is
|
|
103903
|
+
* shown as escaped text instead of injected.
|
|
103904
|
+
*
|
|
103905
|
+
* Runs last, so it sees every producer of the element — `[block:html]`,
|
|
103906
|
+
* `<HTMLBlock>` with a template body or an `html` attribute, and a literal
|
|
103907
|
+
* `<html-block>` — and it is unconditional so an author-supplied
|
|
103908
|
+
* `safeMode="false"` can't opt out.
|
|
103909
|
+
*/
|
|
103910
|
+
const enforceSafeModeHtmlBlocks = () => tree => {
|
|
103911
|
+
visit(tree, 'element', node => {
|
|
103912
|
+
if (isHtmlBlockTag(node.tagName))
|
|
103913
|
+
node.properties.safeMode = 'true';
|
|
103914
|
+
});
|
|
103915
|
+
};
|
|
103916
|
+
|
|
103744
103917
|
;// ./processor/transform/mdxish/anchor-to-jsx.ts
|
|
103745
103918
|
|
|
103746
103919
|
|
|
@@ -105052,6 +105225,7 @@ const collectImportValues = (importDeclarations, registry = defaultModuleRegistr
|
|
|
105052
105225
|
|
|
105053
105226
|
|
|
105054
105227
|
|
|
105228
|
+
|
|
105055
105229
|
/**
|
|
105056
105230
|
* Recursively extract all identifier names introduced by a binding pattern.
|
|
105057
105231
|
* Handles destructuring (`{ a, b }`, `[x, y]`), rest elements (`...rest`),
|
|
@@ -105096,7 +105270,7 @@ const collectExportNames = (declaration) => {
|
|
|
105096
105270
|
* Any evaluation error is consumed and logged. We don't throw because it's
|
|
105097
105271
|
* against the spirit of this engine to be less permissive than MDX needs.
|
|
105098
105272
|
*/
|
|
105099
|
-
const evaluateExports = () => (tree, file) => {
|
|
105273
|
+
const evaluateExports = (opts) => (tree, file) => {
|
|
105100
105274
|
const programBody = [];
|
|
105101
105275
|
const exportNames = [];
|
|
105102
105276
|
const importDeclarations = [];
|
|
@@ -105142,6 +105316,8 @@ const evaluateExports = () => (tree, file) => {
|
|
|
105142
105316
|
// Evaluate the declarations together at once in a single sandboxed Function
|
|
105143
105317
|
try {
|
|
105144
105318
|
const program = { type: 'Program', sourceType: 'module', body: programBody };
|
|
105319
|
+
if (opts?.sanitize)
|
|
105320
|
+
stripJsxTags(program);
|
|
105145
105321
|
buildJsx(program, { runtime: 'classic', pragma: 'React.createElement', pragmaFrag: 'React.Fragment' });
|
|
105146
105322
|
const { value: source } = toJs(program);
|
|
105147
105323
|
// Make sure import values are available in the scope
|
|
@@ -106045,7 +106221,7 @@ const parseApiHeaderTitle = (text) => {
|
|
|
106045
106221
|
* Transform a magicBlock node into final MDAST nodes.
|
|
106046
106222
|
*/
|
|
106047
106223
|
function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
106048
|
-
const { compatibilityMode = false, hardBreaks = true
|
|
106224
|
+
const { compatibilityMode = false, hardBreaks = true } = options;
|
|
106049
106225
|
// Handle empty data by returning placeholder nodes for known block types
|
|
106050
106226
|
// This allows the editor to show appropriate placeholder UI instead of nothing
|
|
106051
106227
|
if (Object.keys(data).length < 1) {
|
|
@@ -106263,7 +106439,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
106263
106439
|
wrapPinnedBlocks({
|
|
106264
106440
|
data: {
|
|
106265
106441
|
hName: 'html-block',
|
|
106266
|
-
hProperties: { html: htmlJson.html, runScripts: compatibilityMode
|
|
106442
|
+
hProperties: { html: htmlJson.html, runScripts: compatibilityMode },
|
|
106267
106443
|
},
|
|
106268
106444
|
type: 'html-block',
|
|
106269
106445
|
}, data),
|
|
@@ -106523,6 +106699,80 @@ const mdxishHtmlBlocks = () => tree => {
|
|
|
106523
106699
|
};
|
|
106524
106700
|
/* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
|
|
106525
106701
|
|
|
106702
|
+
;// ./processor/transform/mdxish/tables/cell-width.ts
|
|
106703
|
+
|
|
106704
|
+
|
|
106705
|
+
// Key must follow `{` or `,` so `minWidth`/`maxWidth` don't match on their suffix
|
|
106706
|
+
const STYLE_OBJECT_WIDTH_REGEX = /[{,]\s*["']?width["']?\s*:\s*(?:"([^"]*)"|'([^']*)'|([^,}]+))/i;
|
|
106707
|
+
// First colon only, so `url(http://…)` values stay intact
|
|
106708
|
+
const CSS_DECLARATION_REGEX = /:(.*)/s;
|
|
106709
|
+
const UNITLESS_NUMBER_REGEX = /^\d+(?:\.\d+)?$/;
|
|
106710
|
+
// HTML reads a `width` attribute as a leading number plus optional `%`, ignoring the rest
|
|
106711
|
+
const HTML_DIMENSION_REGEX = /^\s*(\d+(?:\.\d+)?)\s*(%?)/;
|
|
106712
|
+
/** Bare numbers are pixels. A value carrying declaration separators is not a single width. */
|
|
106713
|
+
const normalizeWidth = (value) => {
|
|
106714
|
+
if (typeof value === 'number')
|
|
106715
|
+
return `${value}px`;
|
|
106716
|
+
if (typeof value !== 'string' || !value.trim())
|
|
106717
|
+
return null;
|
|
106718
|
+
const trimmed = value.trim();
|
|
106719
|
+
if (/[;:{}]/.test(trimmed))
|
|
106720
|
+
return null;
|
|
106721
|
+
return UNITLESS_NUMBER_REGEX.test(trimmed) ? `${trimmed}px` : trimmed;
|
|
106722
|
+
};
|
|
106723
|
+
const widthFromAttribute = (value) => {
|
|
106724
|
+
if (typeof value === 'number')
|
|
106725
|
+
return `${value}px`;
|
|
106726
|
+
if (typeof value !== 'string')
|
|
106727
|
+
return null;
|
|
106728
|
+
const match = HTML_DIMENSION_REGEX.exec(value);
|
|
106729
|
+
return match ? `${match[1]}${match[2] || 'px'}` : null;
|
|
106730
|
+
};
|
|
106731
|
+
const cssPropertyNames = (style) => style
|
|
106732
|
+
.split(';')
|
|
106733
|
+
.map(declaration => declaration.split(CSS_DECLARATION_REGEX)[0].trim().toLowerCase())
|
|
106734
|
+
.filter(Boolean);
|
|
106735
|
+
/** Handles a CSS declaration list and a style object safe mode left as a string (`{ width: "30%" }`). */
|
|
106736
|
+
const widthFromStyleString = (style) => {
|
|
106737
|
+
const trimmed = style.trim();
|
|
106738
|
+
if (trimmed.startsWith('{')) {
|
|
106739
|
+
return STYLE_OBJECT_WIDTH_REGEX.exec(trimmed)?.slice(1).find(Boolean);
|
|
106740
|
+
}
|
|
106741
|
+
return trimmed
|
|
106742
|
+
.split(';')
|
|
106743
|
+
.map(declaration => declaration.split(CSS_DECLARATION_REGEX))
|
|
106744
|
+
.find(([property]) => property.trim().toLowerCase() === 'width')?.[1];
|
|
106745
|
+
};
|
|
106746
|
+
/** Accepts `style={{ width }}`, `style="width: …"` and the hand-authored `width="…"`. */
|
|
106747
|
+
const getCellWidth = (cell) => {
|
|
106748
|
+
const { style, width } = getAttrs(cell);
|
|
106749
|
+
let styleWidth;
|
|
106750
|
+
if (style_object_to_css_isPlainObject(style)) {
|
|
106751
|
+
styleWidth = style.width;
|
|
106752
|
+
}
|
|
106753
|
+
else if (typeof style === 'string') {
|
|
106754
|
+
styleWidth = widthFromStyleString(style);
|
|
106755
|
+
}
|
|
106756
|
+
return normalizeWidth(styleWidth) ?? widthFromAttribute(width);
|
|
106757
|
+
};
|
|
106758
|
+
/** True when the cell's attributes state nothing but its width, which mdast can carry. */
|
|
106759
|
+
const hasOnlyWidthAttributes = (cell) => {
|
|
106760
|
+
if (cell.attributes.length === 0)
|
|
106761
|
+
return false;
|
|
106762
|
+
const { style } = getAttrs(cell);
|
|
106763
|
+
return cell.attributes.every(attribute => {
|
|
106764
|
+
if (!('name' in attribute))
|
|
106765
|
+
return false;
|
|
106766
|
+
if (attribute.name === 'width')
|
|
106767
|
+
return true;
|
|
106768
|
+
if (attribute.name !== 'style')
|
|
106769
|
+
return false;
|
|
106770
|
+
if (style_object_to_css_isPlainObject(style))
|
|
106771
|
+
return Object.keys(style).every(key => key === 'width');
|
|
106772
|
+
return typeof style === 'string' && cssPropertyNames(style).every(property => property === 'width');
|
|
106773
|
+
});
|
|
106774
|
+
};
|
|
106775
|
+
|
|
106526
106776
|
;// ./processor/transform/mdxish/mdxish-jsx-to-mdast.ts
|
|
106527
106777
|
|
|
106528
106778
|
|
|
@@ -106530,6 +106780,7 @@ const mdxishHtmlBlocks = () => tree => {
|
|
|
106530
106780
|
|
|
106531
106781
|
|
|
106532
106782
|
|
|
106783
|
+
|
|
106533
106784
|
function toImageAlign(value) {
|
|
106534
106785
|
if (value === 'left' || value === 'center' || value === 'right') {
|
|
106535
106786
|
return value;
|
|
@@ -106971,10 +107222,40 @@ const wrapBareCellsInRow = (node) => {
|
|
|
106971
107222
|
});
|
|
106972
107223
|
};
|
|
106973
107224
|
/**
|
|
106974
|
-
*
|
|
106975
|
-
*
|
|
107225
|
+
* A lowercase <table> reaches here only because `mdxishTables` kept it as JSX for its
|
|
107226
|
+
* attributes. Widths on first-row cells are the one attribute mdast can carry; anything
|
|
107227
|
+
* else stays JSX so it still renders.
|
|
107228
|
+
*/
|
|
107229
|
+
const lowercaseTableIsConvertible = (jsx) => {
|
|
107230
|
+
if (jsx.attributes.length > 0)
|
|
107231
|
+
return false;
|
|
107232
|
+
const headerCells = new Set();
|
|
107233
|
+
let headerRowSeen = false;
|
|
107234
|
+
let convertible = true;
|
|
107235
|
+
visit(jsx, isMDXElement, (child) => {
|
|
107236
|
+
if (child.name === 'tr' && !headerRowSeen) {
|
|
107237
|
+
headerRowSeen = true;
|
|
107238
|
+
visit(child, mdxish_jsx_to_mdast_isTableCell, cell => {
|
|
107239
|
+
headerCells.add(cell);
|
|
107240
|
+
});
|
|
107241
|
+
}
|
|
107242
|
+
if (child.name === null || !tableTags.has(child.name) || child.attributes.length === 0)
|
|
107243
|
+
return;
|
|
107244
|
+
if (!headerCells.has(child) || !hasOnlyWidthAttributes(child) || getCellWidth(child) === null) {
|
|
107245
|
+
convertible = false;
|
|
107246
|
+
}
|
|
107247
|
+
});
|
|
107248
|
+
return convertible;
|
|
107249
|
+
};
|
|
107250
|
+
/**
|
|
107251
|
+
* Converts a JSX <Table>, or a lowercase <table> carrying only widths, to an MDAST table
|
|
107252
|
+
* node with alignment and widths. Returns null for header-less tables since MDAST always
|
|
107253
|
+
* promotes the first row to <thead>.
|
|
106976
107254
|
*/
|
|
106977
107255
|
const transformTable = (jsx) => {
|
|
107256
|
+
const isLowercase = jsx.name === 'table';
|
|
107257
|
+
if (isLowercase && !lowercaseTableIsConvertible(jsx))
|
|
107258
|
+
return null;
|
|
106978
107259
|
wrapBareCellsInRow(jsx);
|
|
106979
107260
|
let hasThead = false;
|
|
106980
107261
|
visit(jsx, isMDXElement, (child) => {
|
|
@@ -106986,6 +107267,7 @@ const transformTable = (jsx) => {
|
|
|
106986
107267
|
const { align: alignAttr } = getAttrs(jsx);
|
|
106987
107268
|
const align = Array.isArray(alignAttr) ? alignAttr : null;
|
|
106988
107269
|
const rows = [];
|
|
107270
|
+
const widths = [];
|
|
106989
107271
|
visit(jsx, isMDXElement, (child) => {
|
|
106990
107272
|
if (child.name !== 'thead' && child.name !== 'tbody')
|
|
106991
107273
|
return;
|
|
@@ -106993,6 +107275,7 @@ const transformTable = (jsx) => {
|
|
|
106993
107275
|
if (row.name !== 'tr')
|
|
106994
107276
|
return;
|
|
106995
107277
|
const cells = [];
|
|
107278
|
+
const isHeaderRow = rows.length === 0;
|
|
106996
107279
|
visit(row, mdxish_jsx_to_mdast_isTableCell, (cell) => {
|
|
106997
107280
|
const parsedChildren = unwrapSoleParagraph(cell.children);
|
|
106998
107281
|
cells.push({
|
|
@@ -107000,6 +107283,8 @@ const transformTable = (jsx) => {
|
|
|
107000
107283
|
children: parsedChildren,
|
|
107001
107284
|
position: cell.position,
|
|
107002
107285
|
});
|
|
107286
|
+
if (isHeaderRow)
|
|
107287
|
+
widths.push(getCellWidth(cell));
|
|
107003
107288
|
});
|
|
107004
107289
|
rows.push({
|
|
107005
107290
|
type: 'tableRow',
|
|
@@ -107012,11 +107297,16 @@ const transformTable = (jsx) => {
|
|
|
107012
107297
|
const alignArray = align && columnCount > 0
|
|
107013
107298
|
? align.slice(0, columnCount).concat(Array.from({ length: Math.max(0, columnCount - align.length) }, () => null))
|
|
107014
107299
|
: Array.from({ length: columnCount }, () => null);
|
|
107300
|
+
const data = {
|
|
107301
|
+
...(isLowercase && { lowercaseTable: true }),
|
|
107302
|
+
...(widths.some(Boolean) && { widths }),
|
|
107303
|
+
};
|
|
107015
107304
|
return {
|
|
107016
107305
|
type: 'table',
|
|
107017
107306
|
align: alignArray,
|
|
107018
107307
|
position: jsx.position,
|
|
107019
107308
|
children: rows,
|
|
107309
|
+
...(Object.keys(data).length > 0 && { data }),
|
|
107020
107310
|
};
|
|
107021
107311
|
};
|
|
107022
107312
|
/**
|
|
@@ -107046,6 +107336,7 @@ const COMPONENT_MAP = {
|
|
|
107046
107336
|
figure: transformFigure,
|
|
107047
107337
|
Recipe: transformRecipe,
|
|
107048
107338
|
Table: transformTable,
|
|
107339
|
+
table: transformTable,
|
|
107049
107340
|
};
|
|
107050
107341
|
/**
|
|
107051
107342
|
* Transform mdxJsxFlowElement nodes, magic block nodes, and HTML figure elements
|
|
@@ -107521,6 +107812,28 @@ const restoreBooleanProperties = () => tree => {
|
|
|
107521
107812
|
};
|
|
107522
107813
|
|
|
107523
107814
|
|
|
107815
|
+
;// ./processor/transform/mdxish/tables/cell-style.ts
|
|
107816
|
+
/** `left` is the browser default, so it's omitted. Lowercase tables get the HTML string form. */
|
|
107817
|
+
const cellStyle = (align, width = null, { html = false } = {}) => {
|
|
107818
|
+
if (html) {
|
|
107819
|
+
if (!width)
|
|
107820
|
+
return null;
|
|
107821
|
+
return { type: 'mdxJsxAttribute', name: 'style', value: `width: ${width}` };
|
|
107822
|
+
}
|
|
107823
|
+
const declarations = [];
|
|
107824
|
+
if (align && align !== 'left')
|
|
107825
|
+
declarations.push(`textAlign: ${JSON.stringify(align)}`);
|
|
107826
|
+
if (width)
|
|
107827
|
+
declarations.push(`width: ${JSON.stringify(width)}`);
|
|
107828
|
+
if (declarations.length === 0)
|
|
107829
|
+
return null;
|
|
107830
|
+
return {
|
|
107831
|
+
type: 'mdxJsxAttribute',
|
|
107832
|
+
name: 'style',
|
|
107833
|
+
value: { type: 'mdxJsxAttributeValueExpression', value: `{ ${declarations.join(', ')} }` },
|
|
107834
|
+
};
|
|
107835
|
+
};
|
|
107836
|
+
|
|
107524
107837
|
;// ./processor/transform/mdxish/tables/gfm-cell-normalization.ts
|
|
107525
107838
|
/** `<br />` is the only line break a GFM pipe cell can carry, since cells are single-line. */
|
|
107526
107839
|
const createLineBreak = () => ({
|
|
@@ -107559,19 +107872,8 @@ const normalizeCellChildrenForGfm = (cell) => normalizeNodes(cell.children);
|
|
|
107559
107872
|
|
|
107560
107873
|
|
|
107561
107874
|
|
|
107875
|
+
|
|
107562
107876
|
const SELF_CLOSING_JSX_REGEX = /^\s*<[A-Z][^>]*\/>\s*$/;
|
|
107563
|
-
const mdxish_tables_to_jsx_alignToStyle = (align) => {
|
|
107564
|
-
if (!align || align === 'left')
|
|
107565
|
-
return null;
|
|
107566
|
-
return {
|
|
107567
|
-
type: 'mdxJsxAttribute',
|
|
107568
|
-
name: 'style',
|
|
107569
|
-
value: {
|
|
107570
|
-
type: 'mdxJsxAttributeValueExpression',
|
|
107571
|
-
value: `{ textAlign: "${align}" }`,
|
|
107572
|
-
},
|
|
107573
|
-
};
|
|
107574
|
-
};
|
|
107575
107877
|
const mdxish_tables_to_jsx_isTableCell = (node) => ['tableHead', 'tableCell'].includes(node.type);
|
|
107576
107878
|
const mdxish_tables_to_jsx_isLiteral = (node) => 'value' in node;
|
|
107577
107879
|
/**
|
|
@@ -107627,12 +107929,17 @@ const mdxishTablesToJsx = () => tree => {
|
|
|
107627
107929
|
gfmCells.push([cell, normalized]);
|
|
107628
107930
|
return undefined;
|
|
107629
107931
|
});
|
|
107630
|
-
const
|
|
107932
|
+
const widths = table.data?.widths ?? [];
|
|
107933
|
+
const hasWidths = widths.some(Boolean);
|
|
107934
|
+
// Lowercase cells can carry a width but not an alignment, so an aligned table becomes <Table>
|
|
107935
|
+
const hasAlignment = table.align.some(align => align && align !== 'left');
|
|
107936
|
+
const stayLowercase = table.data?.lowercaseTable === true && !hasAlignment;
|
|
107631
107937
|
// We transform to GFM tables if:
|
|
107632
107938
|
// 1. If it doesn't contain complex elements that the syntax can't represent
|
|
107633
|
-
// 2. The original table is not lowercase HTML <table>, because we want to
|
|
107939
|
+
// 2. The original table is not lowercase HTML <table>, because we want to
|
|
107634
107940
|
// preserve original tables as much as possible
|
|
107635
|
-
|
|
107941
|
+
// 3. No column has a width, which GFM can't express
|
|
107942
|
+
if (!requiresJsxTable && !stayLowercase && !hasWidths) {
|
|
107636
107943
|
gfmCells.forEach(([cell, children]) => {
|
|
107637
107944
|
cell.children = children;
|
|
107638
107945
|
});
|
|
@@ -107644,7 +107951,8 @@ const mdxishTablesToJsx = () => tree => {
|
|
|
107644
107951
|
if (!headerRow)
|
|
107645
107952
|
return;
|
|
107646
107953
|
visit(table, mdxish_tables_to_jsx_isTableCell, flattenBreaksToNewlines);
|
|
107647
|
-
const
|
|
107954
|
+
const headerStyles = table.align.map((align, i) => cellStyle(align, widths[i] ?? null, { html: stayLowercase }));
|
|
107955
|
+
const bodyStyles = table.align.map(align => cellStyle(align, null, { html: stayLowercase }));
|
|
107648
107956
|
const head = {
|
|
107649
107957
|
attributes: [],
|
|
107650
107958
|
type: 'mdxJsxFlowElement',
|
|
@@ -107660,7 +107968,7 @@ const mdxishTablesToJsx = () => tree => {
|
|
|
107660
107968
|
type: 'mdxJsxFlowElement',
|
|
107661
107969
|
name: 'th',
|
|
107662
107970
|
children: cell.children,
|
|
107663
|
-
...(
|
|
107971
|
+
...(headerStyles[cellIndex] && { attributes: [headerStyles[cellIndex]] }),
|
|
107664
107972
|
};
|
|
107665
107973
|
}),
|
|
107666
107974
|
},
|
|
@@ -107680,7 +107988,7 @@ const mdxishTablesToJsx = () => tree => {
|
|
|
107680
107988
|
type: 'mdxJsxFlowElement',
|
|
107681
107989
|
name: 'td',
|
|
107682
107990
|
children: cell.children,
|
|
107683
|
-
...(
|
|
107991
|
+
...(bodyStyles[cellIndex] && { attributes: [bodyStyles[cellIndex]] }),
|
|
107684
107992
|
};
|
|
107685
107993
|
}),
|
|
107686
107994
|
};
|
|
@@ -107964,6 +108272,8 @@ function loadComponents() {
|
|
|
107964
108272
|
|
|
107965
108273
|
|
|
107966
108274
|
|
|
108275
|
+
|
|
108276
|
+
|
|
107967
108277
|
|
|
107968
108278
|
|
|
107969
108279
|
|
|
@@ -108091,7 +108401,7 @@ function mdxishMdastToMd(mdast) {
|
|
|
108091
108401
|
* @see .claude/context/MDXish/Processor Overview.md
|
|
108092
108402
|
*/
|
|
108093
108403
|
function mdxish(mdContent, opts = {}) {
|
|
108094
|
-
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, safeMode = false, variables } = opts;
|
|
108404
|
+
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, safeMode = false, sanitize = false, variables, } = opts;
|
|
108095
108405
|
const components = {
|
|
108096
108406
|
...loadComponents(),
|
|
108097
108407
|
...userComponents,
|
|
@@ -108102,7 +108412,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
108102
108412
|
const contentWithoutComments = restoreCodeBlocks(withoutComments, protectedCode);
|
|
108103
108413
|
const { processor, parserReadyContent } = mdxishAstProcessor(contentWithoutComments, opts);
|
|
108104
108414
|
processor
|
|
108105
|
-
.use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
108415
|
+
.use(safeMode ? undefined : evaluate_exports, { sanitize }) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
108106
108416
|
.use(enableHardBreaks ? hard_breaks : undefined) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
|
|
108107
108417
|
.use(safeMode ? undefined : evaluate_expressions, { components, variables }) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
|
|
108108
108418
|
.use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
|
|
@@ -108113,13 +108423,15 @@ function mdxish(mdContent, opts = {}) {
|
|
|
108113
108423
|
.use(restoreBooleanProperties)
|
|
108114
108424
|
.use(safeMode ? undefined : resolve_deferred_attribute_expression_props) // Evaluate deferred attribute expressions on mdx-jsx nodes (now past rehypeRaw's clone)
|
|
108115
108425
|
.use(normalize_mdx_jsx_nodes) // Rewrite `mdx-jsx` back to standard `element` nodes for downstream plugins
|
|
108426
|
+
.use(sanitize ? rehypeStripTags : undefined) // Strip STRIPPED_TAG_NAMES elements; this pipeline has no sanitization step
|
|
108116
108427
|
.use(rehypeFlattenTableCellParagraphs) // Remove <p> wrappers inside table cells to prevent margin issues
|
|
108117
108428
|
.use(mdxish_mermaid) // Add mermaid-render className to pre wrappers
|
|
108118
108429
|
.use(heading_slugs)
|
|
108119
108430
|
.use(rehypeMdxishComponents, {
|
|
108120
108431
|
components,
|
|
108121
108432
|
processMarkdown: (markdown) => mdxish(markdown, opts),
|
|
108122
|
-
})
|
|
108433
|
+
})
|
|
108434
|
+
.use(safeMode ? enforceSafeModeHtmlBlocks : undefined); // Force safeMode on every HTMLBlock; runs last so every producer is covered
|
|
108123
108435
|
const vfile = new VFile({ value: parserReadyContent });
|
|
108124
108436
|
const mdast = processor.parse(parserReadyContent);
|
|
108125
108437
|
const hast = processor.runSync(mdast, vfile);
|
|
@@ -109091,7 +109403,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
|
|
|
109091
109403
|
/******/ // startup
|
|
109092
109404
|
/******/ // Load entry module and return exports
|
|
109093
109405
|
/******/ // This entry module used 'module' so it can't be inlined
|
|
109094
|
-
/******/ let __webpack_exports__ = __webpack_require__(
|
|
109406
|
+
/******/ let __webpack_exports__ = __webpack_require__(9171);
|
|
109095
109407
|
/******/
|
|
109096
109408
|
/******/ return __webpack_exports__;
|
|
109097
109409
|
/******/ })()
|