@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.node.js
CHANGED
|
@@ -19045,7 +19045,7 @@ module.exports = function () {
|
|
|
19045
19045
|
|
|
19046
19046
|
/***/ },
|
|
19047
19047
|
|
|
19048
|
-
/***/
|
|
19048
|
+
/***/ 5789
|
|
19049
19049
|
(module, __webpack_exports__, __webpack_require__) {
|
|
19050
19050
|
|
|
19051
19051
|
"use strict";
|
|
@@ -24743,9 +24743,9 @@ const HTMLBlock = ({ children = '', html: htmlProp, runScripts, safeMode: safeMo
|
|
|
24743
24743
|
const safeMode = typeof safeModeRaw !== 'boolean' ? safeModeRaw === 'true' : safeModeRaw;
|
|
24744
24744
|
const [cleanedHtml, exec] = extractScripts(html);
|
|
24745
24745
|
(0,external_react_.useEffect)(() => {
|
|
24746
|
-
if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts)
|
|
24746
|
+
if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts && !safeMode)
|
|
24747
24747
|
exec();
|
|
24748
|
-
}, [runScripts, exec]);
|
|
24748
|
+
}, [runScripts, safeMode, exec]);
|
|
24749
24749
|
if (nonStringChildren) {
|
|
24750
24750
|
// Fail soft: a non-string child (e.g. JSX that wasn't serialized back to a
|
|
24751
24751
|
// raw string) should never throw, so render the child nodes directly
|
|
@@ -114192,6 +114192,133 @@ const hardBreaks = () => tree => {
|
|
|
114192
114192
|
};
|
|
114193
114193
|
/* harmony default export */ const hard_breaks = (hardBreaks);
|
|
114194
114194
|
|
|
114195
|
+
;// ./processor/plugin/strip-tags.ts
|
|
114196
|
+
|
|
114197
|
+
/**
|
|
114198
|
+
* Tags removed from user-provided content because rendering them executes in the
|
|
114199
|
+
* page. Add an entry to cover another vector; nothing else needs to change.
|
|
114200
|
+
*
|
|
114201
|
+
* Deliberately narrow for now — `<script>` is the only vector this plugin claims
|
|
114202
|
+
* to close. Broader parity with the `md` sanitization schema (`javascript:` URLs,
|
|
114203
|
+
* `on*` handlers, `<iframe>`, `<object>`, …) is tracked in readmeio/markdown#1526.
|
|
114204
|
+
*/
|
|
114205
|
+
const STRIPPED_TAG_NAMES = new Set(['script']);
|
|
114206
|
+
/** JSX names starting with a capital are component references, not literal tags. */
|
|
114207
|
+
const LITERAL_TAG_NAME = /^[a-z]/;
|
|
114208
|
+
/**
|
|
114209
|
+
* Whether a node would render as one of the stripped literal DOM elements.
|
|
114210
|
+
* Browsers match tag names case-insensitively, so `<sCrIpT>` executes too.
|
|
114211
|
+
*
|
|
114212
|
+
* `<Script />` and friends are component references and are left alone.
|
|
114213
|
+
*/
|
|
114214
|
+
const isStrippedTag = (node) => {
|
|
114215
|
+
if (node.type === 'element')
|
|
114216
|
+
return STRIPPED_TAG_NAMES.has(node.tagName.toLowerCase());
|
|
114217
|
+
if (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') {
|
|
114218
|
+
const name = node.name ?? '';
|
|
114219
|
+
return LITERAL_TAG_NAME.test(name) && STRIPPED_TAG_NAMES.has(name.toLowerCase());
|
|
114220
|
+
}
|
|
114221
|
+
return false;
|
|
114222
|
+
};
|
|
114223
|
+
/**
|
|
114224
|
+
* Removes matching elements and their subtrees when `sanitize` is enabled.
|
|
114225
|
+
*
|
|
114226
|
+
* Explicit `HTMLBlock`s are an intentional exception, matching legacy: their
|
|
114227
|
+
* scripts live in a string prop rather than as elements, so they never appear
|
|
114228
|
+
* in SSR HTML, and they only execute client-side when the author opts in via
|
|
114229
|
+
* `runScripts`.
|
|
114230
|
+
*/
|
|
114231
|
+
const rehypeStripTags = () => {
|
|
114232
|
+
return (tree) => {
|
|
114233
|
+
visit(tree, isStrippedTag, (_node, index, parent) => {
|
|
114234
|
+
parent.children.splice(index, 1);
|
|
114235
|
+
return [(/* inlined export .SKIP */"skip"), index];
|
|
114236
|
+
});
|
|
114237
|
+
};
|
|
114238
|
+
};
|
|
114239
|
+
/** The automatic-runtime factories MDX compiles JSX into. */
|
|
114240
|
+
const JSX_FACTORIES = new Set(['_jsx', '_jsxs', '_jsxDEV']);
|
|
114241
|
+
/**
|
|
114242
|
+
* Matches raw JSX and compiled `_jsx('tag', …)` calls. MDX may rewrite literal
|
|
114243
|
+
* tags as `_components.<tag>` so a `components` prop can override them.
|
|
114244
|
+
*/
|
|
114245
|
+
const isStrippedJsxElement = (node) => {
|
|
114246
|
+
if (node.type === 'JSXElement') {
|
|
114247
|
+
const { name } = node.openingElement;
|
|
114248
|
+
if (name.type === 'JSXIdentifier') {
|
|
114249
|
+
return LITERAL_TAG_NAME.test(name.name) && STRIPPED_TAG_NAMES.has(name.name.toLowerCase());
|
|
114250
|
+
}
|
|
114251
|
+
return (name.type === 'JSXMemberExpression' &&
|
|
114252
|
+
name.object.type === 'JSXIdentifier' &&
|
|
114253
|
+
name.object.name === '_components' &&
|
|
114254
|
+
STRIPPED_TAG_NAMES.has(name.property.name.toLowerCase()));
|
|
114255
|
+
}
|
|
114256
|
+
if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && JSX_FACTORIES.has(node.callee.name)) {
|
|
114257
|
+
const [tag] = node.arguments;
|
|
114258
|
+
if (!tag)
|
|
114259
|
+
return false;
|
|
114260
|
+
if (tag.type === 'Literal')
|
|
114261
|
+
return typeof tag.value === 'string' && STRIPPED_TAG_NAMES.has(tag.value.toLowerCase());
|
|
114262
|
+
return (tag.type === 'MemberExpression' &&
|
|
114263
|
+
tag.object.type === 'Identifier' &&
|
|
114264
|
+
tag.object.name === '_components' &&
|
|
114265
|
+
tag.property.type === 'Identifier' &&
|
|
114266
|
+
STRIPPED_TAG_NAMES.has(tag.property.name.toLowerCase()));
|
|
114267
|
+
}
|
|
114268
|
+
return false;
|
|
114269
|
+
};
|
|
114270
|
+
const isEstreeNode = (value) => typeof value === 'object' && value !== null && typeof value.type === 'string';
|
|
114271
|
+
const nullLiteral = () => ({ type: 'Literal', value: null });
|
|
114272
|
+
/**
|
|
114273
|
+
* JSX inside expressions and exports stays in ESTree, outside the HAST pass.
|
|
114274
|
+
* Strip it before evaluation in mdxish or after JSX compilation in MDX.
|
|
114275
|
+
*
|
|
114276
|
+
* Elements are spliced out of JSX children and replaced with `null` anywhere
|
|
114277
|
+
* else, which React renders as nothing.
|
|
114278
|
+
*/
|
|
114279
|
+
const stripJsxTags = (program) => {
|
|
114280
|
+
const walk = (node) => {
|
|
114281
|
+
const record = node;
|
|
114282
|
+
Object.keys(record).forEach(key => {
|
|
114283
|
+
if (key === 'loc' || key === 'range' || key === 'position')
|
|
114284
|
+
return;
|
|
114285
|
+
const value = record[key];
|
|
114286
|
+
if (Array.isArray(value)) {
|
|
114287
|
+
for (let i = value.length - 1; i >= 0; i -= 1) {
|
|
114288
|
+
const child = value[i];
|
|
114289
|
+
if (isEstreeNode(child)) {
|
|
114290
|
+
if (isStrippedJsxElement(child)) {
|
|
114291
|
+
if (key === 'children')
|
|
114292
|
+
value.splice(i, 1);
|
|
114293
|
+
else
|
|
114294
|
+
value[i] = nullLiteral();
|
|
114295
|
+
}
|
|
114296
|
+
else {
|
|
114297
|
+
walk(child);
|
|
114298
|
+
}
|
|
114299
|
+
}
|
|
114300
|
+
}
|
|
114301
|
+
return;
|
|
114302
|
+
}
|
|
114303
|
+
if (!isEstreeNode(value))
|
|
114304
|
+
return;
|
|
114305
|
+
if (isStrippedJsxElement(value)) {
|
|
114306
|
+
record[key] =
|
|
114307
|
+
node.type === 'JSXAttribute' ? { type: 'JSXExpressionContainer', expression: nullLiteral() } : nullLiteral();
|
|
114308
|
+
}
|
|
114309
|
+
else {
|
|
114310
|
+
walk(value);
|
|
114311
|
+
}
|
|
114312
|
+
});
|
|
114313
|
+
};
|
|
114314
|
+
walk(program);
|
|
114315
|
+
return program;
|
|
114316
|
+
};
|
|
114317
|
+
/** `stripJsxTags` as a recma plugin, for the `mdx` compile pipeline. */
|
|
114318
|
+
const recmaStripTags = () => tree => {
|
|
114319
|
+
stripJsxTags(tree);
|
|
114320
|
+
};
|
|
114321
|
+
|
|
114195
114322
|
;// ./node_modules/estree-util-value-to-estree/dist/estree-util-value-to-estree.js
|
|
114196
114323
|
/**
|
|
114197
114324
|
* Create an ESTree identifier node for a given name.
|
|
@@ -116929,6 +117056,7 @@ const tocHastToMdx = (toc, components, variables) => {
|
|
|
116929
117056
|
|
|
116930
117057
|
|
|
116931
117058
|
|
|
117059
|
+
|
|
116932
117060
|
const sanitizeSchema = cjs_default()(defaultSchema, {
|
|
116933
117061
|
attributes: {
|
|
116934
117062
|
a: ['target'],
|
|
@@ -116937,7 +117065,10 @@ const sanitizeSchema = cjs_default()(defaultSchema, {
|
|
|
116937
117065
|
href: ['doc', 'ref', 'blog', 'changelog', 'page'],
|
|
116938
117066
|
},
|
|
116939
117067
|
});
|
|
116940
|
-
const compile_compile = (text, { components = {}, missingComponents, copyButtons, useTailwind, hardBreaks,
|
|
117068
|
+
const compile_compile = (text, { components = {}, missingComponents, copyButtons, useTailwind, hardBreaks, sanitize = false,
|
|
117069
|
+
// Pulled out of `...opts` so a caller's plugins extend the pipeline instead of
|
|
117070
|
+
// replacing it — otherwise the spread below would drop `rehypeStripTags`.
|
|
117071
|
+
remarkPlugins: userRemarkPlugins, rehypePlugins: userRehypePlugins, recmaPlugins: userRecmaPlugins, ...opts } = {}) => {
|
|
116941
117072
|
// Destructure at runtime to avoid circular dependency issues
|
|
116942
117073
|
const { codeTabsTransformer, ...transforms } = defaultTransforms;
|
|
116943
117074
|
const remarkPlugins = [
|
|
@@ -116955,7 +117086,12 @@ const compile_compile = (text, { components = {}, missingComponents, copyButtons
|
|
|
116955
117086
|
if (useTailwind) {
|
|
116956
117087
|
remarkPlugins.push([transform_tailwind, { components }]);
|
|
116957
117088
|
}
|
|
116958
|
-
|
|
117089
|
+
remarkPlugins.push(...(userRemarkPlugins ?? []));
|
|
117090
|
+
const rehypePlugins = [
|
|
117091
|
+
...ast_processor_rehypePlugins,
|
|
117092
|
+
[rehypeToc, { components }],
|
|
117093
|
+
...(userRehypePlugins ?? []),
|
|
117094
|
+
];
|
|
116959
117095
|
if (opts.format === 'md') {
|
|
116960
117096
|
/**
|
|
116961
117097
|
* When running in `md` format we need to sanitize the content for security reasons.
|
|
@@ -116973,12 +117109,23 @@ const compile_compile = (text, { components = {}, missingComponents, copyButtons
|
|
|
116973
117109
|
]);
|
|
116974
117110
|
rehypePlugins.push([rehypeSanitize, sanitizeSchema]);
|
|
116975
117111
|
}
|
|
117112
|
+
const recmaPlugins = [...(userRecmaPlugins ?? [])];
|
|
117113
|
+
// In `mdx` format a literal <script> parses as JSX rather than raw HTML, so it
|
|
117114
|
+
// bypasses sanitization entirely — strip it in every format. Must stay last:
|
|
117115
|
+
// in `md` format raw HTML isn't an element until `rehypeRaw` has run. A tag
|
|
117116
|
+
// inside an expression or export is code, not an element, so the recma pass
|
|
117117
|
+
// catches it in the compiled program instead.
|
|
117118
|
+
if (sanitize) {
|
|
117119
|
+
rehypePlugins.push(rehypeStripTags);
|
|
117120
|
+
recmaPlugins.push(recmaStripTags);
|
|
117121
|
+
}
|
|
116976
117122
|
try {
|
|
116977
117123
|
const vfile = compileSync(text, {
|
|
116978
117124
|
outputFormat: 'function-body',
|
|
116979
117125
|
providerImportSource: '#',
|
|
116980
117126
|
remarkPlugins,
|
|
116981
117127
|
rehypePlugins,
|
|
117128
|
+
recmaPlugins,
|
|
116982
117129
|
...opts,
|
|
116983
117130
|
});
|
|
116984
117131
|
const string = String(vfile)
|
|
@@ -123924,6 +124071,32 @@ const mdxComponentHandlers = {
|
|
|
123924
124071
|
[NodeTypes.htmlBlock]: htmlBlockHandler,
|
|
123925
124072
|
};
|
|
123926
124073
|
|
|
124074
|
+
;// ./processor/plugin/safe-mode-html-blocks.ts
|
|
124075
|
+
|
|
124076
|
+
|
|
124077
|
+
/**
|
|
124078
|
+
* Whether a tag name renders through the `HTMLBlock` component. Mirrors the
|
|
124079
|
+
* separator-agnostic, case-insensitive lookup in `getComponentName` and the
|
|
124080
|
+
* rehype-react component map, so `html-block`, `HTMLBlock`, `HtmlBlock` and
|
|
124081
|
+
* `htmlblock` all match.
|
|
124082
|
+
*/
|
|
124083
|
+
const isHtmlBlockTag = (tagName) => toPascalCase(tagName).toLowerCase() === 'htmlblock';
|
|
124084
|
+
/**
|
|
124085
|
+
* Forces `safeMode` on every element that renders through `HTMLBlock` so it is
|
|
124086
|
+
* shown as escaped text instead of injected.
|
|
124087
|
+
*
|
|
124088
|
+
* Runs last, so it sees every producer of the element — `[block:html]`,
|
|
124089
|
+
* `<HTMLBlock>` with a template body or an `html` attribute, and a literal
|
|
124090
|
+
* `<html-block>` — and it is unconditional so an author-supplied
|
|
124091
|
+
* `safeMode="false"` can't opt out.
|
|
124092
|
+
*/
|
|
124093
|
+
const enforceSafeModeHtmlBlocks = () => tree => {
|
|
124094
|
+
visit(tree, 'element', node => {
|
|
124095
|
+
if (isHtmlBlockTag(node.tagName))
|
|
124096
|
+
node.properties.safeMode = 'true';
|
|
124097
|
+
});
|
|
124098
|
+
};
|
|
124099
|
+
|
|
123927
124100
|
;// ./processor/transform/mdxish/anchor-to-jsx.ts
|
|
123928
124101
|
|
|
123929
124102
|
|
|
@@ -125235,6 +125408,7 @@ const collectImportValues = (importDeclarations, registry = defaultModuleRegistr
|
|
|
125235
125408
|
|
|
125236
125409
|
|
|
125237
125410
|
|
|
125411
|
+
|
|
125238
125412
|
/**
|
|
125239
125413
|
* Recursively extract all identifier names introduced by a binding pattern.
|
|
125240
125414
|
* Handles destructuring (`{ a, b }`, `[x, y]`), rest elements (`...rest`),
|
|
@@ -125279,7 +125453,7 @@ const collectExportNames = (declaration) => {
|
|
|
125279
125453
|
* Any evaluation error is consumed and logged. We don't throw because it's
|
|
125280
125454
|
* against the spirit of this engine to be less permissive than MDX needs.
|
|
125281
125455
|
*/
|
|
125282
|
-
const evaluateExports = () => (tree, file) => {
|
|
125456
|
+
const evaluateExports = (opts) => (tree, file) => {
|
|
125283
125457
|
const programBody = [];
|
|
125284
125458
|
const exportNames = [];
|
|
125285
125459
|
const importDeclarations = [];
|
|
@@ -125325,6 +125499,8 @@ const evaluateExports = () => (tree, file) => {
|
|
|
125325
125499
|
// Evaluate the declarations together at once in a single sandboxed Function
|
|
125326
125500
|
try {
|
|
125327
125501
|
const program = { type: 'Program', sourceType: 'module', body: programBody };
|
|
125502
|
+
if (opts?.sanitize)
|
|
125503
|
+
stripJsxTags(program);
|
|
125328
125504
|
buildJsx(program, { runtime: 'classic', pragma: 'React.createElement', pragmaFrag: 'React.Fragment' });
|
|
125329
125505
|
const { value: source } = toJs(program);
|
|
125330
125506
|
// Make sure import values are available in the scope
|
|
@@ -126228,7 +126404,7 @@ const parseApiHeaderTitle = (text) => {
|
|
|
126228
126404
|
* Transform a magicBlock node into final MDAST nodes.
|
|
126229
126405
|
*/
|
|
126230
126406
|
function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
126231
|
-
const { compatibilityMode = false, hardBreaks = true
|
|
126407
|
+
const { compatibilityMode = false, hardBreaks = true } = options;
|
|
126232
126408
|
// Handle empty data by returning placeholder nodes for known block types
|
|
126233
126409
|
// This allows the editor to show appropriate placeholder UI instead of nothing
|
|
126234
126410
|
if (Object.keys(data).length < 1) {
|
|
@@ -126446,7 +126622,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
126446
126622
|
wrapPinnedBlocks({
|
|
126447
126623
|
data: {
|
|
126448
126624
|
hName: 'html-block',
|
|
126449
|
-
hProperties: { html: htmlJson.html, runScripts: compatibilityMode
|
|
126625
|
+
hProperties: { html: htmlJson.html, runScripts: compatibilityMode },
|
|
126450
126626
|
},
|
|
126451
126627
|
type: 'html-block',
|
|
126452
126628
|
}, data),
|
|
@@ -126706,6 +126882,80 @@ const mdxishHtmlBlocks = () => tree => {
|
|
|
126706
126882
|
};
|
|
126707
126883
|
/* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
|
|
126708
126884
|
|
|
126885
|
+
;// ./processor/transform/mdxish/tables/cell-width.ts
|
|
126886
|
+
|
|
126887
|
+
|
|
126888
|
+
// Key must follow `{` or `,` so `minWidth`/`maxWidth` don't match on their suffix
|
|
126889
|
+
const STYLE_OBJECT_WIDTH_REGEX = /[{,]\s*["']?width["']?\s*:\s*(?:"([^"]*)"|'([^']*)'|([^,}]+))/i;
|
|
126890
|
+
// First colon only, so `url(http://…)` values stay intact
|
|
126891
|
+
const CSS_DECLARATION_REGEX = /:(.*)/s;
|
|
126892
|
+
const UNITLESS_NUMBER_REGEX = /^\d+(?:\.\d+)?$/;
|
|
126893
|
+
// HTML reads a `width` attribute as a leading number plus optional `%`, ignoring the rest
|
|
126894
|
+
const HTML_DIMENSION_REGEX = /^\s*(\d+(?:\.\d+)?)\s*(%?)/;
|
|
126895
|
+
/** Bare numbers are pixels. A value carrying declaration separators is not a single width. */
|
|
126896
|
+
const normalizeWidth = (value) => {
|
|
126897
|
+
if (typeof value === 'number')
|
|
126898
|
+
return `${value}px`;
|
|
126899
|
+
if (typeof value !== 'string' || !value.trim())
|
|
126900
|
+
return null;
|
|
126901
|
+
const trimmed = value.trim();
|
|
126902
|
+
if (/[;:{}]/.test(trimmed))
|
|
126903
|
+
return null;
|
|
126904
|
+
return UNITLESS_NUMBER_REGEX.test(trimmed) ? `${trimmed}px` : trimmed;
|
|
126905
|
+
};
|
|
126906
|
+
const widthFromAttribute = (value) => {
|
|
126907
|
+
if (typeof value === 'number')
|
|
126908
|
+
return `${value}px`;
|
|
126909
|
+
if (typeof value !== 'string')
|
|
126910
|
+
return null;
|
|
126911
|
+
const match = HTML_DIMENSION_REGEX.exec(value);
|
|
126912
|
+
return match ? `${match[1]}${match[2] || 'px'}` : null;
|
|
126913
|
+
};
|
|
126914
|
+
const cssPropertyNames = (style) => style
|
|
126915
|
+
.split(';')
|
|
126916
|
+
.map(declaration => declaration.split(CSS_DECLARATION_REGEX)[0].trim().toLowerCase())
|
|
126917
|
+
.filter(Boolean);
|
|
126918
|
+
/** Handles a CSS declaration list and a style object safe mode left as a string (`{ width: "30%" }`). */
|
|
126919
|
+
const widthFromStyleString = (style) => {
|
|
126920
|
+
const trimmed = style.trim();
|
|
126921
|
+
if (trimmed.startsWith('{')) {
|
|
126922
|
+
return STYLE_OBJECT_WIDTH_REGEX.exec(trimmed)?.slice(1).find(Boolean);
|
|
126923
|
+
}
|
|
126924
|
+
return trimmed
|
|
126925
|
+
.split(';')
|
|
126926
|
+
.map(declaration => declaration.split(CSS_DECLARATION_REGEX))
|
|
126927
|
+
.find(([property]) => property.trim().toLowerCase() === 'width')?.[1];
|
|
126928
|
+
};
|
|
126929
|
+
/** Accepts `style={{ width }}`, `style="width: …"` and the hand-authored `width="…"`. */
|
|
126930
|
+
const getCellWidth = (cell) => {
|
|
126931
|
+
const { style, width } = getAttrs(cell);
|
|
126932
|
+
let styleWidth;
|
|
126933
|
+
if (style_object_to_css_isPlainObject(style)) {
|
|
126934
|
+
styleWidth = style.width;
|
|
126935
|
+
}
|
|
126936
|
+
else if (typeof style === 'string') {
|
|
126937
|
+
styleWidth = widthFromStyleString(style);
|
|
126938
|
+
}
|
|
126939
|
+
return normalizeWidth(styleWidth) ?? widthFromAttribute(width);
|
|
126940
|
+
};
|
|
126941
|
+
/** True when the cell's attributes state nothing but its width, which mdast can carry. */
|
|
126942
|
+
const hasOnlyWidthAttributes = (cell) => {
|
|
126943
|
+
if (cell.attributes.length === 0)
|
|
126944
|
+
return false;
|
|
126945
|
+
const { style } = getAttrs(cell);
|
|
126946
|
+
return cell.attributes.every(attribute => {
|
|
126947
|
+
if (!('name' in attribute))
|
|
126948
|
+
return false;
|
|
126949
|
+
if (attribute.name === 'width')
|
|
126950
|
+
return true;
|
|
126951
|
+
if (attribute.name !== 'style')
|
|
126952
|
+
return false;
|
|
126953
|
+
if (style_object_to_css_isPlainObject(style))
|
|
126954
|
+
return Object.keys(style).every(key => key === 'width');
|
|
126955
|
+
return typeof style === 'string' && cssPropertyNames(style).every(property => property === 'width');
|
|
126956
|
+
});
|
|
126957
|
+
};
|
|
126958
|
+
|
|
126709
126959
|
;// ./processor/transform/mdxish/mdxish-jsx-to-mdast.ts
|
|
126710
126960
|
|
|
126711
126961
|
|
|
@@ -126713,6 +126963,7 @@ const mdxishHtmlBlocks = () => tree => {
|
|
|
126713
126963
|
|
|
126714
126964
|
|
|
126715
126965
|
|
|
126966
|
+
|
|
126716
126967
|
function toImageAlign(value) {
|
|
126717
126968
|
if (value === 'left' || value === 'center' || value === 'right') {
|
|
126718
126969
|
return value;
|
|
@@ -127154,10 +127405,40 @@ const wrapBareCellsInRow = (node) => {
|
|
|
127154
127405
|
});
|
|
127155
127406
|
};
|
|
127156
127407
|
/**
|
|
127157
|
-
*
|
|
127158
|
-
*
|
|
127408
|
+
* A lowercase <table> reaches here only because `mdxishTables` kept it as JSX for its
|
|
127409
|
+
* attributes. Widths on first-row cells are the one attribute mdast can carry; anything
|
|
127410
|
+
* else stays JSX so it still renders.
|
|
127411
|
+
*/
|
|
127412
|
+
const lowercaseTableIsConvertible = (jsx) => {
|
|
127413
|
+
if (jsx.attributes.length > 0)
|
|
127414
|
+
return false;
|
|
127415
|
+
const headerCells = new Set();
|
|
127416
|
+
let headerRowSeen = false;
|
|
127417
|
+
let convertible = true;
|
|
127418
|
+
visit(jsx, isMDXElement, (child) => {
|
|
127419
|
+
if (child.name === 'tr' && !headerRowSeen) {
|
|
127420
|
+
headerRowSeen = true;
|
|
127421
|
+
visit(child, mdxish_jsx_to_mdast_isTableCell, cell => {
|
|
127422
|
+
headerCells.add(cell);
|
|
127423
|
+
});
|
|
127424
|
+
}
|
|
127425
|
+
if (child.name === null || !tableTags.has(child.name) || child.attributes.length === 0)
|
|
127426
|
+
return;
|
|
127427
|
+
if (!headerCells.has(child) || !hasOnlyWidthAttributes(child) || getCellWidth(child) === null) {
|
|
127428
|
+
convertible = false;
|
|
127429
|
+
}
|
|
127430
|
+
});
|
|
127431
|
+
return convertible;
|
|
127432
|
+
};
|
|
127433
|
+
/**
|
|
127434
|
+
* Converts a JSX <Table>, or a lowercase <table> carrying only widths, to an MDAST table
|
|
127435
|
+
* node with alignment and widths. Returns null for header-less tables since MDAST always
|
|
127436
|
+
* promotes the first row to <thead>.
|
|
127159
127437
|
*/
|
|
127160
127438
|
const transformTable = (jsx) => {
|
|
127439
|
+
const isLowercase = jsx.name === 'table';
|
|
127440
|
+
if (isLowercase && !lowercaseTableIsConvertible(jsx))
|
|
127441
|
+
return null;
|
|
127161
127442
|
wrapBareCellsInRow(jsx);
|
|
127162
127443
|
let hasThead = false;
|
|
127163
127444
|
visit(jsx, isMDXElement, (child) => {
|
|
@@ -127169,6 +127450,7 @@ const transformTable = (jsx) => {
|
|
|
127169
127450
|
const { align: alignAttr } = getAttrs(jsx);
|
|
127170
127451
|
const align = Array.isArray(alignAttr) ? alignAttr : null;
|
|
127171
127452
|
const rows = [];
|
|
127453
|
+
const widths = [];
|
|
127172
127454
|
visit(jsx, isMDXElement, (child) => {
|
|
127173
127455
|
if (child.name !== 'thead' && child.name !== 'tbody')
|
|
127174
127456
|
return;
|
|
@@ -127176,6 +127458,7 @@ const transformTable = (jsx) => {
|
|
|
127176
127458
|
if (row.name !== 'tr')
|
|
127177
127459
|
return;
|
|
127178
127460
|
const cells = [];
|
|
127461
|
+
const isHeaderRow = rows.length === 0;
|
|
127179
127462
|
visit(row, mdxish_jsx_to_mdast_isTableCell, (cell) => {
|
|
127180
127463
|
const parsedChildren = unwrapSoleParagraph(cell.children);
|
|
127181
127464
|
cells.push({
|
|
@@ -127183,6 +127466,8 @@ const transformTable = (jsx) => {
|
|
|
127183
127466
|
children: parsedChildren,
|
|
127184
127467
|
position: cell.position,
|
|
127185
127468
|
});
|
|
127469
|
+
if (isHeaderRow)
|
|
127470
|
+
widths.push(getCellWidth(cell));
|
|
127186
127471
|
});
|
|
127187
127472
|
rows.push({
|
|
127188
127473
|
type: 'tableRow',
|
|
@@ -127195,11 +127480,16 @@ const transformTable = (jsx) => {
|
|
|
127195
127480
|
const alignArray = align && columnCount > 0
|
|
127196
127481
|
? align.slice(0, columnCount).concat(Array.from({ length: Math.max(0, columnCount - align.length) }, () => null))
|
|
127197
127482
|
: Array.from({ length: columnCount }, () => null);
|
|
127483
|
+
const data = {
|
|
127484
|
+
...(isLowercase && { lowercaseTable: true }),
|
|
127485
|
+
...(widths.some(Boolean) && { widths }),
|
|
127486
|
+
};
|
|
127198
127487
|
return {
|
|
127199
127488
|
type: 'table',
|
|
127200
127489
|
align: alignArray,
|
|
127201
127490
|
position: jsx.position,
|
|
127202
127491
|
children: rows,
|
|
127492
|
+
...(Object.keys(data).length > 0 && { data }),
|
|
127203
127493
|
};
|
|
127204
127494
|
};
|
|
127205
127495
|
/**
|
|
@@ -127229,6 +127519,7 @@ const COMPONENT_MAP = {
|
|
|
127229
127519
|
figure: transformFigure,
|
|
127230
127520
|
Recipe: transformRecipe,
|
|
127231
127521
|
Table: transformTable,
|
|
127522
|
+
table: transformTable,
|
|
127232
127523
|
};
|
|
127233
127524
|
/**
|
|
127234
127525
|
* Transform mdxJsxFlowElement nodes, magic block nodes, and HTML figure elements
|
|
@@ -127704,6 +127995,28 @@ const restoreBooleanProperties = () => tree => {
|
|
|
127704
127995
|
};
|
|
127705
127996
|
|
|
127706
127997
|
|
|
127998
|
+
;// ./processor/transform/mdxish/tables/cell-style.ts
|
|
127999
|
+
/** `left` is the browser default, so it's omitted. Lowercase tables get the HTML string form. */
|
|
128000
|
+
const cellStyle = (align, width = null, { html = false } = {}) => {
|
|
128001
|
+
if (html) {
|
|
128002
|
+
if (!width)
|
|
128003
|
+
return null;
|
|
128004
|
+
return { type: 'mdxJsxAttribute', name: 'style', value: `width: ${width}` };
|
|
128005
|
+
}
|
|
128006
|
+
const declarations = [];
|
|
128007
|
+
if (align && align !== 'left')
|
|
128008
|
+
declarations.push(`textAlign: ${JSON.stringify(align)}`);
|
|
128009
|
+
if (width)
|
|
128010
|
+
declarations.push(`width: ${JSON.stringify(width)}`);
|
|
128011
|
+
if (declarations.length === 0)
|
|
128012
|
+
return null;
|
|
128013
|
+
return {
|
|
128014
|
+
type: 'mdxJsxAttribute',
|
|
128015
|
+
name: 'style',
|
|
128016
|
+
value: { type: 'mdxJsxAttributeValueExpression', value: `{ ${declarations.join(', ')} }` },
|
|
128017
|
+
};
|
|
128018
|
+
};
|
|
128019
|
+
|
|
127707
128020
|
;// ./processor/transform/mdxish/tables/gfm-cell-normalization.ts
|
|
127708
128021
|
/** `<br />` is the only line break a GFM pipe cell can carry, since cells are single-line. */
|
|
127709
128022
|
const createLineBreak = () => ({
|
|
@@ -127742,19 +128055,8 @@ const normalizeCellChildrenForGfm = (cell) => normalizeNodes(cell.children);
|
|
|
127742
128055
|
|
|
127743
128056
|
|
|
127744
128057
|
|
|
128058
|
+
|
|
127745
128059
|
const SELF_CLOSING_JSX_REGEX = /^\s*<[A-Z][^>]*\/>\s*$/;
|
|
127746
|
-
const mdxish_tables_to_jsx_alignToStyle = (align) => {
|
|
127747
|
-
if (!align || align === 'left')
|
|
127748
|
-
return null;
|
|
127749
|
-
return {
|
|
127750
|
-
type: 'mdxJsxAttribute',
|
|
127751
|
-
name: 'style',
|
|
127752
|
-
value: {
|
|
127753
|
-
type: 'mdxJsxAttributeValueExpression',
|
|
127754
|
-
value: `{ textAlign: "${align}" }`,
|
|
127755
|
-
},
|
|
127756
|
-
};
|
|
127757
|
-
};
|
|
127758
128060
|
const mdxish_tables_to_jsx_isTableCell = (node) => ['tableHead', 'tableCell'].includes(node.type);
|
|
127759
128061
|
const mdxish_tables_to_jsx_isLiteral = (node) => 'value' in node;
|
|
127760
128062
|
/**
|
|
@@ -127810,12 +128112,17 @@ const mdxishTablesToJsx = () => tree => {
|
|
|
127810
128112
|
gfmCells.push([cell, normalized]);
|
|
127811
128113
|
return undefined;
|
|
127812
128114
|
});
|
|
127813
|
-
const
|
|
128115
|
+
const widths = table.data?.widths ?? [];
|
|
128116
|
+
const hasWidths = widths.some(Boolean);
|
|
128117
|
+
// Lowercase cells can carry a width but not an alignment, so an aligned table becomes <Table>
|
|
128118
|
+
const hasAlignment = table.align.some(align => align && align !== 'left');
|
|
128119
|
+
const stayLowercase = table.data?.lowercaseTable === true && !hasAlignment;
|
|
127814
128120
|
// We transform to GFM tables if:
|
|
127815
128121
|
// 1. If it doesn't contain complex elements that the syntax can't represent
|
|
127816
|
-
// 2. The original table is not lowercase HTML <table>, because we want to
|
|
128122
|
+
// 2. The original table is not lowercase HTML <table>, because we want to
|
|
127817
128123
|
// preserve original tables as much as possible
|
|
127818
|
-
|
|
128124
|
+
// 3. No column has a width, which GFM can't express
|
|
128125
|
+
if (!requiresJsxTable && !stayLowercase && !hasWidths) {
|
|
127819
128126
|
gfmCells.forEach(([cell, children]) => {
|
|
127820
128127
|
cell.children = children;
|
|
127821
128128
|
});
|
|
@@ -127827,7 +128134,8 @@ const mdxishTablesToJsx = () => tree => {
|
|
|
127827
128134
|
if (!headerRow)
|
|
127828
128135
|
return;
|
|
127829
128136
|
visit(table, mdxish_tables_to_jsx_isTableCell, flattenBreaksToNewlines);
|
|
127830
|
-
const
|
|
128137
|
+
const headerStyles = table.align.map((align, i) => cellStyle(align, widths[i] ?? null, { html: stayLowercase }));
|
|
128138
|
+
const bodyStyles = table.align.map(align => cellStyle(align, null, { html: stayLowercase }));
|
|
127831
128139
|
const head = {
|
|
127832
128140
|
attributes: [],
|
|
127833
128141
|
type: 'mdxJsxFlowElement',
|
|
@@ -127843,7 +128151,7 @@ const mdxishTablesToJsx = () => tree => {
|
|
|
127843
128151
|
type: 'mdxJsxFlowElement',
|
|
127844
128152
|
name: 'th',
|
|
127845
128153
|
children: cell.children,
|
|
127846
|
-
...(
|
|
128154
|
+
...(headerStyles[cellIndex] && { attributes: [headerStyles[cellIndex]] }),
|
|
127847
128155
|
};
|
|
127848
128156
|
}),
|
|
127849
128157
|
},
|
|
@@ -127863,7 +128171,7 @@ const mdxishTablesToJsx = () => tree => {
|
|
|
127863
128171
|
type: 'mdxJsxFlowElement',
|
|
127864
128172
|
name: 'td',
|
|
127865
128173
|
children: cell.children,
|
|
127866
|
-
...(
|
|
128174
|
+
...(bodyStyles[cellIndex] && { attributes: [bodyStyles[cellIndex]] }),
|
|
127867
128175
|
};
|
|
127868
128176
|
}),
|
|
127869
128177
|
};
|
|
@@ -128147,6 +128455,8 @@ function loadComponents() {
|
|
|
128147
128455
|
|
|
128148
128456
|
|
|
128149
128457
|
|
|
128458
|
+
|
|
128459
|
+
|
|
128150
128460
|
|
|
128151
128461
|
|
|
128152
128462
|
|
|
@@ -128274,7 +128584,7 @@ function mdxishMdastToMd(mdast) {
|
|
|
128274
128584
|
* @see .claude/context/MDXish/Processor Overview.md
|
|
128275
128585
|
*/
|
|
128276
128586
|
function mdxish(mdContent, opts = {}) {
|
|
128277
|
-
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, safeMode = false, variables } = opts;
|
|
128587
|
+
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, safeMode = false, sanitize = false, variables, } = opts;
|
|
128278
128588
|
const components = {
|
|
128279
128589
|
...loadComponents(),
|
|
128280
128590
|
...userComponents,
|
|
@@ -128285,7 +128595,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
128285
128595
|
const contentWithoutComments = restoreCodeBlocks(withoutComments, protectedCode);
|
|
128286
128596
|
const { processor, parserReadyContent } = mdxishAstProcessor(contentWithoutComments, opts);
|
|
128287
128597
|
processor
|
|
128288
|
-
.use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
128598
|
+
.use(safeMode ? undefined : evaluate_exports, { sanitize }) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
128289
128599
|
.use(enableHardBreaks ? hard_breaks : undefined) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
|
|
128290
128600
|
.use(safeMode ? undefined : evaluate_expressions, { components, variables }) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
|
|
128291
128601
|
.use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
|
|
@@ -128296,13 +128606,15 @@ function mdxish(mdContent, opts = {}) {
|
|
|
128296
128606
|
.use(restoreBooleanProperties)
|
|
128297
128607
|
.use(safeMode ? undefined : resolve_deferred_attribute_expression_props) // Evaluate deferred attribute expressions on mdx-jsx nodes (now past rehypeRaw's clone)
|
|
128298
128608
|
.use(normalize_mdx_jsx_nodes) // Rewrite `mdx-jsx` back to standard `element` nodes for downstream plugins
|
|
128609
|
+
.use(sanitize ? rehypeStripTags : undefined) // Strip STRIPPED_TAG_NAMES elements; this pipeline has no sanitization step
|
|
128299
128610
|
.use(rehypeFlattenTableCellParagraphs) // Remove <p> wrappers inside table cells to prevent margin issues
|
|
128300
128611
|
.use(mdxish_mermaid) // Add mermaid-render className to pre wrappers
|
|
128301
128612
|
.use(heading_slugs)
|
|
128302
128613
|
.use(rehypeMdxishComponents, {
|
|
128303
128614
|
components,
|
|
128304
128615
|
processMarkdown: (markdown) => mdxish(markdown, opts),
|
|
128305
|
-
})
|
|
128616
|
+
})
|
|
128617
|
+
.use(safeMode ? enforceSafeModeHtmlBlocks : undefined); // Force safeMode on every HTMLBlock; runs last so every producer is covered
|
|
128306
128618
|
const vfile = new VFile({ value: parserReadyContent });
|
|
128307
128619
|
const mdast = processor.parse(parserReadyContent);
|
|
128308
128620
|
const hast = processor.runSync(mdast, vfile);
|
|
@@ -129333,7 +129645,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
|
|
|
129333
129645
|
/******/ // startup
|
|
129334
129646
|
/******/ // Load entry module and return exports
|
|
129335
129647
|
/******/ // This entry module used 'module' so it can't be inlined
|
|
129336
|
-
/******/ let __webpack_exports__ = __webpack_require__(
|
|
129648
|
+
/******/ let __webpack_exports__ = __webpack_require__(5789);
|
|
129337
129649
|
/******/ module.exports = __webpack_exports__;
|
|
129338
129650
|
/******/
|
|
129339
129651
|
/******/ })()
|