@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/dist/main.js CHANGED
@@ -11423,7 +11423,7 @@ module.exports = function () {
11423
11423
 
11424
11424
  /***/ },
11425
11425
 
11426
- /***/ 7070
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, ...opts } = {}) => {
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
- const rehypePlugins = [...ast_processor_rehypePlugins, [rehypeToc, { components }]];
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, safeMode = false } = options;
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, safeMode },
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
- * Converts a JSX <Table> element to an MDAST table node with alignment.
106975
- * Returns null for header-less tables since MDAST always promotes the first row to <thead>.
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 stayLowercase = table.data?.lowercaseTable === true;
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
- if (!requiresJsxTable && !stayLowercase) {
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 styles = table.align.map(mdxish_tables_to_jsx_alignToStyle);
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
- ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
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
- ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
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__(7070);
109406
+ /******/ let __webpack_exports__ = __webpack_require__(9171);
109095
109407
  /******/
109096
109408
  /******/ return __webpack_exports__;
109097
109409
  /******/ })()