@readme/markdown 15.8.0 → 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.
@@ -0,0 +1,12 @@
1
+ import type { Root } from 'hast';
2
+ import type { Transformer } from 'unified';
3
+ /**
4
+ * Forces `safeMode` on every element that renders through `HTMLBlock` so it is
5
+ * shown as escaped text instead of injected.
6
+ *
7
+ * Runs last, so it sees every producer of the element — `[block:html]`,
8
+ * `<HTMLBlock>` with a template body or an `html` attribute, and a literal
9
+ * `<html-block>` — and it is unconditional so an author-supplied
10
+ * `safeMode="false"` can't opt out.
11
+ */
12
+ export declare const enforceSafeModeHtmlBlocks: () => Transformer<Root, Root>;
@@ -64,7 +64,6 @@ export interface RecipeJson extends MagicBlockJson {
64
64
  export interface MagicBlockTransformerOptions {
65
65
  compatibilityMode?: boolean;
66
66
  hardBreaks?: boolean;
67
- safeMode?: boolean;
68
67
  }
69
68
  /**
70
69
  * Magic block image node structure (output from magicBlockTransformer).
@@ -0,0 +1,7 @@
1
+ import type { MdxJsxAttribute } from 'mdast-util-mdx-jsx';
2
+ type CellAlign = 'center' | 'left' | 'right' | null;
3
+ /** `left` is the browser default, so it's omitted. Lowercase tables get the HTML string form. */
4
+ export declare const cellStyle: (align: CellAlign, width?: string | null, { html }?: {
5
+ html?: boolean;
6
+ }) => MdxJsxAttribute | null;
7
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx';
2
+ /** Bare numbers are pixels. A value carrying declaration separators is not a single width. */
3
+ export declare const normalizeWidth: (value: unknown) => string | null;
4
+ /** Handles a CSS declaration list and a style object safe mode left as a string (`{ width: "30%" }`). */
5
+ export declare const widthFromStyleString: (style: string) => string | undefined;
6
+ /** Accepts `style={{ width }}`, `style="width: …"` and the hand-authored `width="…"`. */
7
+ export declare const getCellWidth: (cell: MdxJsxFlowElement | MdxJsxTextElement) => string | null;
8
+ /** True when the cell's attributes state nothing but its width, which mdast can carry. */
9
+ export declare const hasOnlyWidthAttributes: (cell: MdxJsxFlowElement | MdxJsxTextElement) => boolean;
@@ -19045,7 +19045,7 @@ module.exports = function () {
19045
19045
 
19046
19046
  /***/ },
19047
19047
 
19048
- /***/ 6829
19048
+ /***/ 8900
19049
19049
  (module, __webpack_exports__, __webpack_require__) {
19050
19050
 
19051
19051
  "use strict";
@@ -92178,9 +92178,9 @@ const HTMLBlock = ({ children = '', html: htmlProp, runScripts, safeMode: safeMo
92178
92178
  const safeMode = typeof safeModeRaw !== 'boolean' ? safeModeRaw === 'true' : safeModeRaw;
92179
92179
  const [cleanedHtml, exec] = extractScripts(html);
92180
92180
  (0,external_react_.useEffect)(() => {
92181
- if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts)
92181
+ if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts && !safeMode)
92182
92182
  exec();
92183
- }, [runScripts, exec]);
92183
+ }, [runScripts, safeMode, exec]);
92184
92184
  if (nonStringChildren) {
92185
92185
  // Fail soft: a non-string child (e.g. JSX that wasn't serialized back to a
92186
92186
  // raw string) should never throw, so render the child nodes directly
@@ -122425,6 +122425,32 @@ const mdxComponentHandlers = {
122425
122425
  [NodeTypes.htmlBlock]: htmlBlockHandler,
122426
122426
  };
122427
122427
 
122428
+ ;// ./processor/plugin/safe-mode-html-blocks.ts
122429
+
122430
+
122431
+ /**
122432
+ * Whether a tag name renders through the `HTMLBlock` component. Mirrors the
122433
+ * separator-agnostic, case-insensitive lookup in `getComponentName` and the
122434
+ * rehype-react component map, so `html-block`, `HTMLBlock`, `HtmlBlock` and
122435
+ * `htmlblock` all match.
122436
+ */
122437
+ const isHtmlBlockTag = (tagName) => toPascalCase(tagName).toLowerCase() === 'htmlblock';
122438
+ /**
122439
+ * Forces `safeMode` on every element that renders through `HTMLBlock` so it is
122440
+ * shown as escaped text instead of injected.
122441
+ *
122442
+ * Runs last, so it sees every producer of the element — `[block:html]`,
122443
+ * `<HTMLBlock>` with a template body or an `html` attribute, and a literal
122444
+ * `<html-block>` — and it is unconditional so an author-supplied
122445
+ * `safeMode="false"` can't opt out.
122446
+ */
122447
+ const enforceSafeModeHtmlBlocks = () => tree => {
122448
+ visit(tree, 'element', node => {
122449
+ if (isHtmlBlockTag(node.tagName))
122450
+ node.properties.safeMode = 'true';
122451
+ });
122452
+ };
122453
+
122428
122454
  ;// ./processor/transform/mdxish/anchor-to-jsx.ts
122429
122455
  /* unused harmony import specifier */ var anchor_to_jsx_visit;
122430
122456
  /* unused harmony import specifier */ var anchor_to_jsx_NodeTypes;
@@ -126147,7 +126173,7 @@ const parseApiHeaderTitle = (text) => {
126147
126173
  * Transform a magicBlock node into final MDAST nodes.
126148
126174
  */
126149
126175
  function transformMagicBlock(blockType, data, rawValue, options = {}) {
126150
- const { compatibilityMode = false, hardBreaks = true, safeMode = false } = options;
126176
+ const { compatibilityMode = false, hardBreaks = true } = options;
126151
126177
  // Handle empty data by returning placeholder nodes for known block types
126152
126178
  // This allows the editor to show appropriate placeholder UI instead of nothing
126153
126179
  if (Object.keys(data).length < 1) {
@@ -126365,7 +126391,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
126365
126391
  wrapPinnedBlocks({
126366
126392
  data: {
126367
126393
  hName: 'html-block',
126368
- hProperties: { html: htmlJson.html, runScripts: compatibilityMode, safeMode },
126394
+ hProperties: { html: htmlJson.html, runScripts: compatibilityMode },
126369
126395
  },
126370
126396
  type: 'html-block',
126371
126397
  }, data),
@@ -126625,6 +126651,80 @@ const mdxishHtmlBlocks = () => tree => {
126625
126651
  };
126626
126652
  /* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
126627
126653
 
126654
+ ;// ./processor/transform/mdxish/tables/cell-width.ts
126655
+
126656
+
126657
+ // Key must follow `{` or `,` so `minWidth`/`maxWidth` don't match on their suffix
126658
+ const STYLE_OBJECT_WIDTH_REGEX = /[{,]\s*["']?width["']?\s*:\s*(?:"([^"]*)"|'([^']*)'|([^,}]+))/i;
126659
+ // First colon only, so `url(http://…)` values stay intact
126660
+ const CSS_DECLARATION_REGEX = /:(.*)/s;
126661
+ const UNITLESS_NUMBER_REGEX = /^\d+(?:\.\d+)?$/;
126662
+ // HTML reads a `width` attribute as a leading number plus optional `%`, ignoring the rest
126663
+ const HTML_DIMENSION_REGEX = /^\s*(\d+(?:\.\d+)?)\s*(%?)/;
126664
+ /** Bare numbers are pixels. A value carrying declaration separators is not a single width. */
126665
+ const normalizeWidth = (value) => {
126666
+ if (typeof value === 'number')
126667
+ return `${value}px`;
126668
+ if (typeof value !== 'string' || !value.trim())
126669
+ return null;
126670
+ const trimmed = value.trim();
126671
+ if (/[;:{}]/.test(trimmed))
126672
+ return null;
126673
+ return UNITLESS_NUMBER_REGEX.test(trimmed) ? `${trimmed}px` : trimmed;
126674
+ };
126675
+ const widthFromAttribute = (value) => {
126676
+ if (typeof value === 'number')
126677
+ return `${value}px`;
126678
+ if (typeof value !== 'string')
126679
+ return null;
126680
+ const match = HTML_DIMENSION_REGEX.exec(value);
126681
+ return match ? `${match[1]}${match[2] || 'px'}` : null;
126682
+ };
126683
+ const cssPropertyNames = (style) => style
126684
+ .split(';')
126685
+ .map(declaration => declaration.split(CSS_DECLARATION_REGEX)[0].trim().toLowerCase())
126686
+ .filter(Boolean);
126687
+ /** Handles a CSS declaration list and a style object safe mode left as a string (`{ width: "30%" }`). */
126688
+ const widthFromStyleString = (style) => {
126689
+ const trimmed = style.trim();
126690
+ if (trimmed.startsWith('{')) {
126691
+ return STYLE_OBJECT_WIDTH_REGEX.exec(trimmed)?.slice(1).find(Boolean);
126692
+ }
126693
+ return trimmed
126694
+ .split(';')
126695
+ .map(declaration => declaration.split(CSS_DECLARATION_REGEX))
126696
+ .find(([property]) => property.trim().toLowerCase() === 'width')?.[1];
126697
+ };
126698
+ /** Accepts `style={{ width }}`, `style="width: …"` and the hand-authored `width="…"`. */
126699
+ const getCellWidth = (cell) => {
126700
+ const { style, width } = getAttrs(cell);
126701
+ let styleWidth;
126702
+ if (style_object_to_css_isPlainObject(style)) {
126703
+ styleWidth = style.width;
126704
+ }
126705
+ else if (typeof style === 'string') {
126706
+ styleWidth = widthFromStyleString(style);
126707
+ }
126708
+ return normalizeWidth(styleWidth) ?? widthFromAttribute(width);
126709
+ };
126710
+ /** True when the cell's attributes state nothing but its width, which mdast can carry. */
126711
+ const hasOnlyWidthAttributes = (cell) => {
126712
+ if (cell.attributes.length === 0)
126713
+ return false;
126714
+ const { style } = getAttrs(cell);
126715
+ return cell.attributes.every(attribute => {
126716
+ if (!('name' in attribute))
126717
+ return false;
126718
+ if (attribute.name === 'width')
126719
+ return true;
126720
+ if (attribute.name !== 'style')
126721
+ return false;
126722
+ if (style_object_to_css_isPlainObject(style))
126723
+ return Object.keys(style).every(key => key === 'width');
126724
+ return typeof style === 'string' && cssPropertyNames(style).every(property => property === 'width');
126725
+ });
126726
+ };
126727
+
126628
126728
  ;// ./processor/transform/mdxish/mdxish-jsx-to-mdast.ts
126629
126729
 
126630
126730
 
@@ -126632,6 +126732,7 @@ const mdxishHtmlBlocks = () => tree => {
126632
126732
 
126633
126733
 
126634
126734
 
126735
+
126635
126736
  function toImageAlign(value) {
126636
126737
  if (value === 'left' || value === 'center' || value === 'right') {
126637
126738
  return value;
@@ -127073,10 +127174,40 @@ const wrapBareCellsInRow = (node) => {
127073
127174
  });
127074
127175
  };
127075
127176
  /**
127076
- * Converts a JSX <Table> element to an MDAST table node with alignment.
127077
- * Returns null for header-less tables since MDAST always promotes the first row to <thead>.
127177
+ * A lowercase <table> reaches here only because `mdxishTables` kept it as JSX for its
127178
+ * attributes. Widths on first-row cells are the one attribute mdast can carry; anything
127179
+ * else stays JSX so it still renders.
127180
+ */
127181
+ const lowercaseTableIsConvertible = (jsx) => {
127182
+ if (jsx.attributes.length > 0)
127183
+ return false;
127184
+ const headerCells = new Set();
127185
+ let headerRowSeen = false;
127186
+ let convertible = true;
127187
+ visit(jsx, isMDXElement, (child) => {
127188
+ if (child.name === 'tr' && !headerRowSeen) {
127189
+ headerRowSeen = true;
127190
+ visit(child, mdxish_jsx_to_mdast_isTableCell, cell => {
127191
+ headerCells.add(cell);
127192
+ });
127193
+ }
127194
+ if (child.name === null || !tableTags.has(child.name) || child.attributes.length === 0)
127195
+ return;
127196
+ if (!headerCells.has(child) || !hasOnlyWidthAttributes(child) || getCellWidth(child) === null) {
127197
+ convertible = false;
127198
+ }
127199
+ });
127200
+ return convertible;
127201
+ };
127202
+ /**
127203
+ * Converts a JSX <Table>, or a lowercase <table> carrying only widths, to an MDAST table
127204
+ * node with alignment and widths. Returns null for header-less tables since MDAST always
127205
+ * promotes the first row to <thead>.
127078
127206
  */
127079
127207
  const transformTable = (jsx) => {
127208
+ const isLowercase = jsx.name === 'table';
127209
+ if (isLowercase && !lowercaseTableIsConvertible(jsx))
127210
+ return null;
127080
127211
  wrapBareCellsInRow(jsx);
127081
127212
  let hasThead = false;
127082
127213
  visit(jsx, isMDXElement, (child) => {
@@ -127088,6 +127219,7 @@ const transformTable = (jsx) => {
127088
127219
  const { align: alignAttr } = getAttrs(jsx);
127089
127220
  const align = Array.isArray(alignAttr) ? alignAttr : null;
127090
127221
  const rows = [];
127222
+ const widths = [];
127091
127223
  visit(jsx, isMDXElement, (child) => {
127092
127224
  if (child.name !== 'thead' && child.name !== 'tbody')
127093
127225
  return;
@@ -127095,6 +127227,7 @@ const transformTable = (jsx) => {
127095
127227
  if (row.name !== 'tr')
127096
127228
  return;
127097
127229
  const cells = [];
127230
+ const isHeaderRow = rows.length === 0;
127098
127231
  visit(row, mdxish_jsx_to_mdast_isTableCell, (cell) => {
127099
127232
  const parsedChildren = unwrapSoleParagraph(cell.children);
127100
127233
  cells.push({
@@ -127102,6 +127235,8 @@ const transformTable = (jsx) => {
127102
127235
  children: parsedChildren,
127103
127236
  position: cell.position,
127104
127237
  });
127238
+ if (isHeaderRow)
127239
+ widths.push(getCellWidth(cell));
127105
127240
  });
127106
127241
  rows.push({
127107
127242
  type: 'tableRow',
@@ -127114,11 +127249,16 @@ const transformTable = (jsx) => {
127114
127249
  const alignArray = align && columnCount > 0
127115
127250
  ? align.slice(0, columnCount).concat(Array.from({ length: Math.max(0, columnCount - align.length) }, () => null))
127116
127251
  : Array.from({ length: columnCount }, () => null);
127252
+ const data = {
127253
+ ...(isLowercase && { lowercaseTable: true }),
127254
+ ...(widths.some(Boolean) && { widths }),
127255
+ };
127117
127256
  return {
127118
127257
  type: 'table',
127119
127258
  align: alignArray,
127120
127259
  position: jsx.position,
127121
127260
  children: rows,
127261
+ ...(Object.keys(data).length > 0 && { data }),
127122
127262
  };
127123
127263
  };
127124
127264
  /**
@@ -127148,6 +127288,7 @@ const COMPONENT_MAP = {
127148
127288
  figure: transformFigure,
127149
127289
  Recipe: transformRecipe,
127150
127290
  Table: transformTable,
127291
+ table: transformTable,
127151
127292
  };
127152
127293
  /**
127153
127294
  * Transform mdxJsxFlowElement nodes, magic block nodes, and HTML figure elements
@@ -127627,25 +127768,15 @@ const restoreBooleanProperties = () => tree => {
127627
127768
  /* unused harmony import specifier */ var mdxish_tables_to_jsx_phrasing;
127628
127769
  /* unused harmony import specifier */ var mdxish_tables_to_jsx_visit;
127629
127770
  /* unused harmony import specifier */ var mdxish_tables_to_jsx_NodeTypes;
127771
+ /* unused harmony import specifier */ var cellStyle;
127630
127772
  /* unused harmony import specifier */ var hasChildren;
127631
127773
  /* unused harmony import specifier */ var normalizeCellChildrenForGfm;
127632
127774
 
127633
127775
 
127634
127776
 
127635
127777
 
127778
+
127636
127779
  const SELF_CLOSING_JSX_REGEX = /^\s*<[A-Z][^>]*\/>\s*$/;
127637
- const mdxish_tables_to_jsx_alignToStyle = (align) => {
127638
- if (!align || align === 'left')
127639
- return null;
127640
- return {
127641
- type: 'mdxJsxAttribute',
127642
- name: 'style',
127643
- value: {
127644
- type: 'mdxJsxAttributeValueExpression',
127645
- value: `{ textAlign: "${align}" }`,
127646
- },
127647
- };
127648
- };
127649
127780
  const mdxish_tables_to_jsx_isTableCell = (node) => ['tableHead', 'tableCell'].includes(node.type);
127650
127781
  const mdxish_tables_to_jsx_isLiteral = (node) => 'value' in node;
127651
127782
  /**
@@ -127701,12 +127832,17 @@ const mdxishTablesToJsx = () => tree => {
127701
127832
  gfmCells.push([cell, normalized]);
127702
127833
  return undefined;
127703
127834
  });
127704
- const stayLowercase = table.data?.lowercaseTable === true;
127835
+ const widths = table.data?.widths ?? [];
127836
+ const hasWidths = widths.some(Boolean);
127837
+ // Lowercase cells can carry a width but not an alignment, so an aligned table becomes <Table>
127838
+ const hasAlignment = table.align.some(align => align && align !== 'left');
127839
+ const stayLowercase = table.data?.lowercaseTable === true && !hasAlignment;
127705
127840
  // We transform to GFM tables if:
127706
127841
  // 1. If it doesn't contain complex elements that the syntax can't represent
127707
- // 2. The original table is not lowercase HTML <table>, because we want to
127842
+ // 2. The original table is not lowercase HTML <table>, because we want to
127708
127843
  // preserve original tables as much as possible
127709
- if (!requiresJsxTable && !stayLowercase) {
127844
+ // 3. No column has a width, which GFM can't express
127845
+ if (!requiresJsxTable && !stayLowercase && !hasWidths) {
127710
127846
  gfmCells.forEach(([cell, children]) => {
127711
127847
  cell.children = children;
127712
127848
  });
@@ -127718,7 +127854,8 @@ const mdxishTablesToJsx = () => tree => {
127718
127854
  if (!headerRow)
127719
127855
  return;
127720
127856
  mdxish_tables_to_jsx_visit(table, mdxish_tables_to_jsx_isTableCell, flattenBreaksToNewlines);
127721
- const styles = table.align.map(mdxish_tables_to_jsx_alignToStyle);
127857
+ const headerStyles = table.align.map((align, i) => cellStyle(align, widths[i] ?? null, { html: stayLowercase }));
127858
+ const bodyStyles = table.align.map(align => cellStyle(align, null, { html: stayLowercase }));
127722
127859
  const head = {
127723
127860
  attributes: [],
127724
127861
  type: 'mdxJsxFlowElement',
@@ -127734,7 +127871,7 @@ const mdxishTablesToJsx = () => tree => {
127734
127871
  type: 'mdxJsxFlowElement',
127735
127872
  name: 'th',
127736
127873
  children: cell.children,
127737
- ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
127874
+ ...(headerStyles[cellIndex] && { attributes: [headerStyles[cellIndex]] }),
127738
127875
  };
127739
127876
  }),
127740
127877
  },
@@ -127754,7 +127891,7 @@ const mdxishTablesToJsx = () => tree => {
127754
127891
  type: 'mdxJsxFlowElement',
127755
127892
  name: 'td',
127756
127893
  children: cell.children,
127757
- ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
127894
+ ...(bodyStyles[cellIndex] && { attributes: [bodyStyles[cellIndex]] }),
127758
127895
  };
127759
127896
  }),
127760
127897
  };
@@ -128035,6 +128172,7 @@ function loadComponents() {
128035
128172
 
128036
128173
 
128037
128174
 
128175
+
128038
128176
 
128039
128177
 
128040
128178
  const defaultTransformers = [
@@ -128190,7 +128328,8 @@ function mdxish(mdContent, opts = {}) {
128190
128328
  .use(rehypeMdxishComponents, {
128191
128329
  components,
128192
128330
  processMarkdown: (markdown) => mdxish(markdown, opts),
128193
- });
128331
+ })
128332
+ .use(safeMode ? enforceSafeModeHtmlBlocks : undefined); // Force safeMode on every HTMLBlock; runs last so every producer is covered
128194
128333
  const vfile = new VFile({ value: parserReadyContent });
128195
128334
  const mdast = processor.parse(parserReadyContent);
128196
128335
  const hast = processor.runSync(mdast, vfile);
@@ -129266,7 +129405,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
129266
129405
  /******/ // startup
129267
129406
  /******/ // Load entry module and return exports
129268
129407
  /******/ // This entry module used 'module' so it can't be inlined
129269
- /******/ let __webpack_exports__ = __webpack_require__(6829);
129408
+ /******/ let __webpack_exports__ = __webpack_require__(8900);
129270
129409
  /******/ module.exports = __webpack_exports__;
129271
129410
  /******/
129272
129411
  /******/ })()