@readme/markdown 14.10.3 → 14.11.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/index.d.ts CHANGED
@@ -7,6 +7,7 @@ declare const utils: {
7
7
  calloutIcons: {};
8
8
  };
9
9
  export { compile, exports, FLOW_TYPES, hast, INLINE_ONLY_PARENT_TYPES, run, mdast, mdastV6, mdx, mdxish, mdxishAstProcessor, mdxishMdastToMd, mdxishTags, extractToc, migrate, mix, plain, renderMdxish, remarkPlugins, stripComments, tags, } from './lib';
10
+ export type { MdxishOpts, RenderMdxishOpts, RunOpts } from './lib';
10
11
  export { default as Owlmoji } from './lib/owlmoji';
11
12
  export { Components, utils };
12
13
  export { tailwindCompiler } from './utils/tailwind-compiler';
@@ -16,6 +16,7 @@ export { default as plain } from './plain';
16
16
  export { default as renderMdxish } from './renderMdxish';
17
17
  export type { RenderMdxishOpts } from './renderMdxish';
18
18
  export { default as run } from './run';
19
+ export type { RunOpts } from './run';
19
20
  export { default as tags } from './tags';
20
21
  export { default as mdxishTags } from './mdxishTags';
21
22
  export { default as stripComments } from './stripComments';
@@ -0,0 +1 @@
1
+ export { mdxExpressionLenient } from './syntax';
@@ -0,0 +1,2 @@
1
+ import type { Extension } from 'micromark-util-types';
2
+ export declare function mdxExpressionLenient(): Extension;
package/dist/main.js CHANGED
@@ -102166,12 +102166,60 @@ function restoreSnakeCase(placeholderName, mapping) {
102166
102166
  return matchingKey ? mapping[matchingKey] : placeholderName;
102167
102167
  }
102168
102168
 
102169
+ ;// ./processor/transform/mdxish/resolve-esm-imports.ts
102170
+
102171
+ // We provide React as a default module so that components can use hooks
102172
+ // and it's a common use case. Add other common libraries here.
102173
+ const defaultModuleRegistry = { react: (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default()) };
102174
+ const isRecord = (value) => typeof value === 'object' && value !== null;
102175
+ // Get the value of a named export from a module
102176
+ const getModuleExport = (module, importedName) => isRecord(module) && importedName in module ? module[importedName] : undefined;
102177
+ /**
102178
+ * Turn `import` declarations into a flat map of binding → value.
102179
+ *
102180
+ * A "binding" is a mapping of a local name to the value of it in the module exports
102181
+ * Example: `import { useState } from 'react'` yields `{ useState: React.useState }`.
102182
+ *
102183
+ * Unsupported specifiers are skipped with a warning rather than throwing, so a
102184
+ * single unknown import can't break the rest of the document.
102185
+ */
102186
+ const collectImportValues = (importDeclarations, registry = defaultModuleRegistry) => {
102187
+ const importValues = {};
102188
+ importDeclarations.forEach(declaration => {
102189
+ const libraryName = declaration.source.value;
102190
+ if (typeof libraryName !== 'string')
102191
+ return;
102192
+ if (!(libraryName in registry)) {
102193
+ // eslint-disable-next-line no-console
102194
+ console.warn(`[WARNING] Cannot resolve import "${libraryName}"; it is not a supported library.`);
102195
+ return;
102196
+ }
102197
+ const module = registry[libraryName];
102198
+ declaration.specifiers.forEach(spec => {
102199
+ // Default (`import React`) and namespace (`import * as React`) both bind
102200
+ // the whole module under the local name.
102201
+ if (spec.type === 'ImportDefaultSpecifier' || spec.type === 'ImportNamespaceSpecifier') {
102202
+ importValues[spec.local.name] = module;
102203
+ }
102204
+ else if (spec.type === 'ImportSpecifier') {
102205
+ // Named imports (e.g. `import { useState } from 'react'`, useState is the import name)
102206
+ const importName = spec.imported.type === 'Identifier' ? spec.imported.name : spec.imported.value;
102207
+ if (typeof importName === 'string') {
102208
+ importValues[spec.local.name] = getModuleExport(module, importName);
102209
+ }
102210
+ }
102211
+ });
102212
+ });
102213
+ return importValues;
102214
+ };
102215
+
102169
102216
  ;// ./processor/transform/mdxish/evaluate-exports.ts
102170
102217
 
102171
102218
 
102172
102219
 
102173
102220
 
102174
102221
 
102222
+
102175
102223
  /**
102176
102224
  * Recursively extract all identifier names introduced by a binding pattern.
102177
102225
  * Handles destructuring (`{ a, b }`, `[x, y]`), rest elements (`...rest`),
@@ -102219,6 +102267,7 @@ const collectExportNames = (declaration) => {
102219
102267
  const evaluateExports = () => (tree, file) => {
102220
102268
  const programBody = [];
102221
102269
  const exportNames = [];
102270
+ const importDeclarations = [];
102222
102271
  const nodesToRemove = [];
102223
102272
  visit(tree, isMDXEsm, (node, index, parent) => {
102224
102273
  if (parent && typeof index === 'number') {
@@ -102232,6 +102281,10 @@ const evaluateExports = () => (tree, file) => {
102232
102281
  // handled the same as a named export — the inner declaration carries the
102233
102282
  // binding name
102234
102283
  estreeBody.forEach(statement => {
102284
+ if (statement.type === 'ImportDeclaration') {
102285
+ importDeclarations.push(statement);
102286
+ return;
102287
+ }
102235
102288
  let declaration = null;
102236
102289
  if (statement.type === 'ExportNamedDeclaration' && statement.declaration) {
102237
102290
  declaration = statement.declaration;
@@ -102259,9 +102312,11 @@ const evaluateExports = () => (tree, file) => {
102259
102312
  const program = { type: 'Program', sourceType: 'module', body: programBody };
102260
102313
  buildJsx(program, { runtime: 'classic', pragma: 'React.createElement', pragmaFrag: 'React.Fragment' });
102261
102314
  const { value: source } = toJs(program);
102315
+ // Make sure import values are available in the scope
102316
+ const importValues = collectImportValues(importDeclarations);
102262
102317
  // Evaluate and build on the expression source
102263
102318
  // Use react to compile JSX codes
102264
- const evaluatedExports = evaluate(`(() => { ${source}\nreturn { ${exportNames.join(', ')} }; })()`, { React: (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default()) });
102319
+ const evaluatedExports = evaluate(`(() => { ${source}\nreturn { ${exportNames.join(', ')} }; })()`, { React: (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default()), ...importValues });
102265
102320
  Object.assign(scope, evaluatedExports);
102266
102321
  }
102267
102322
  catch (error) {
@@ -104215,214 +104270,21 @@ const normalizeMdxJsxNodes = () => tree => {
104215
104270
  */
104216
104271
  const JSX_COMMENT_REGEX = /\{\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\/\}/g;
104217
104272
 
104218
- ;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
104219
-
104273
+ ;// ./processor/transform/mdxish/remove-jsx-comments.ts
104220
104274
 
104221
104275
  /**
104222
104276
  * Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
104223
104277
  *
104224
- * @param content
104225
- * @returns Content with JSX comments removed
104226
- * @example
104278
+ * `@param` content
104279
+ * `@returns` Content with JSX comments removed
104280
+ * `@example`
104227
104281
  * ```typescript
104228
- * removeJSXComments('Text { /* comment *\/ } more text')
104229
- * // Returns: 'Text more text'
104282
+ * removeJSXComments('Text {/* comment *\/} more text') // => 'Text more text'
104230
104283
  * ```
104231
104284
  */
104232
104285
  function removeJSXComments(content) {
104233
104286
  return content.replace(JSX_COMMENT_REGEX, '');
104234
104287
  }
104235
- const HTML_ELEM_PLACEHOLDER_PREFIX = '___MDXISH_HTML_ELEM_';
104236
- const HTML_ELEM_PLACEHOLDER = new RegExp(`${HTML_ELEM_PLACEHOLDER_PREFIX}(\\d+)___`, 'g');
104237
- // Matches an HTML element that starts at a line boundary and ends at a line boundary.
104238
- // Allows optional leading indentation and lazily matches until the same closing tag.
104239
- const BLOCK_HTML_RE = /(?<=^|\n)[ \t]*<([a-z][a-zA-Z0-9]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>[ \t]*(?=\n|$)/g;
104240
- /**
104241
- * Hides line-anchored HTML elements from the brace-escaping pass so we don't leak `\{`
104242
- * into rendered output (rehypeRaw renders the `\` literally, e.g. `<div>{foo</div>`).
104243
- *
104244
- * One carve-out: if an interior line at column 0 has bare text containing `{`, mdxish
104245
- * parses that line as a paragraph and the mdxExpression step would throw without an
104246
- * escape — so we leave that case to the brace balancer.
104247
- */
104248
- function protectHTMLElements(content) {
104249
- const htmlElements = [];
104250
- const protectedContent = content.replace(BLOCK_HTML_RE, match => {
104251
- // Look at the lines between the open and close tags. If any of them starts
104252
- // at column 0 with bare text (not whitespace, not another tag) and contains
104253
- // `{`, mdxish will parse that line as a paragraph and the brace as an MDX
104254
- // expression, which would throw an error. So we let the brace balancer escape it.
104255
- // Otherwise, we need to extract the sequence to protect it from the brace escaping.
104256
- const interior = match.split('\n').slice(1, -1);
104257
- const hazard = interior.some(line => line.length > 0 && line[0] !== ' ' && line[0] !== '\t' && line[0] !== '<' && line.includes('{'));
104258
- if (hazard)
104259
- return match;
104260
- htmlElements.push(match);
104261
- return `${HTML_ELEM_PLACEHOLDER_PREFIX}${htmlElements.length - 1}___`;
104262
- });
104263
- return { htmlElements, protectedContent };
104264
- }
104265
- function restoreHTMLElements(content, htmlElements) {
104266
- if (htmlElements.length === 0)
104267
- return content;
104268
- return content.replace(HTML_ELEM_PLACEHOLDER, (_m, idx) => htmlElements[parseInt(idx, 10)]);
104269
- }
104270
- const ESM_DECLARATION_START = /^(?:export|import)\b/;
104271
- /**
104272
- * Whether a line begins a top-level `export`/`import` declaration. Its braces
104273
- * are valid JS owned by the mdxjsEsm tokenizer (which tolerates blank lines),
104274
- * so escaping them would corrupt the source and break acorn parsing.
104275
- */
104276
- function startsEsmDeclaration(chars, lineStart) {
104277
- return ESM_DECLARATION_START.test(chars.slice(lineStart, lineStart + 7).join(''));
104278
- }
104279
- function isBlankLine(chars, lineStart) {
104280
- for (let i = lineStart; i < chars.length; i += 1) {
104281
- if (chars[i] === '\n')
104282
- return true;
104283
- if (chars[i] !== ' ' && chars[i] !== '\t')
104284
- return false;
104285
- }
104286
- return true;
104287
- }
104288
- /**
104289
- * Escapes unbalanced and paragraph-spanning braces so MDX doesn't trip on them.
104290
- */
104291
- function escapeProblematicBraces(content) {
104292
- const { htmlElements, protectedContent } = protectHTMLElements(content);
104293
- let strDelim = null;
104294
- let strEscaped = false;
104295
- // Track position of last newline (outside strings) to detect blank lines
104296
- // -2 means no recent newline
104297
- let lastNewlinePos = -2;
104298
- // Character state machine trackers
104299
- const toEscape = new Set();
104300
- // Convert to array of Unicode code points so that emojis and multi-byte characters are correctly tracked
104301
- const chars = Array.from(protectedContent);
104302
- const openStack = [];
104303
- // Whether the current top-level statement is an `export`/`import` declaration.
104304
- let insideEsmDeclaration = false;
104305
- for (let i = 0; i < chars.length; i += 1) {
104306
- const ch = chars[i];
104307
- // At a top-level (brace depth 0) line start, decide whether we're inside an
104308
- // ESM declaration. The flag persists across the declaration's own lines.
104309
- if (openStack.length === 0 && (i === 0 || chars[i - 1] === '\n')) {
104310
- if (startsEsmDeclaration(chars, i))
104311
- insideEsmDeclaration = true;
104312
- else if (isBlankLine(chars, i))
104313
- insideEsmDeclaration = false;
104314
- }
104315
- // Track string delimiters inside expressions to ignore braces within them
104316
- if (openStack.length > 0) {
104317
- if (strDelim) {
104318
- if (strEscaped)
104319
- strEscaped = false;
104320
- else if (ch === '\\')
104321
- strEscaped = true;
104322
- else if (ch === strDelim)
104323
- strDelim = null;
104324
- // eslint-disable-next-line no-continue
104325
- continue;
104326
- }
104327
- if (ch === '"' || ch === "'" || ch === '`') {
104328
- strDelim = ch;
104329
- // eslint-disable-next-line no-continue
104330
- continue;
104331
- }
104332
- if (ch === '\n') {
104333
- if (lastNewlinePos >= 0) {
104334
- const between = chars.slice(lastNewlinePos + 1, i).join('');
104335
- if (/^[ \t]*$/.test(between)) {
104336
- openStack.forEach(entry => { entry.hasBlankLine = true; });
104337
- }
104338
- }
104339
- lastNewlinePos = i;
104340
- }
104341
- }
104342
- // Skip already-escaped braces (odd run of preceding backslashes).
104343
- if (ch === '{' || ch === '}') {
104344
- let bs = 0;
104345
- for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
104346
- bs += 1;
104347
- if (bs % 2 === 1) {
104348
- // eslint-disable-next-line no-continue
104349
- continue;
104350
- }
104351
- }
104352
- if (ch === '{') {
104353
- // `=` (after whitespace) before `{` ⇒ JSX attribute expression. The
104354
- // mdxComponent tokenizer captures the whole component, so blank lines
104355
- // inside attribute values are harmless. Nested `{` inherits the flag.
104356
- let isAttrExpr = false;
104357
- for (let j = i - 1; j >= 0; j -= 1) {
104358
- const pc = chars[j];
104359
- if (pc === '=') {
104360
- isAttrExpr = true;
104361
- break;
104362
- }
104363
- if (pc !== ' ' && pc !== '\t')
104364
- break;
104365
- }
104366
- // Nested `{ ... }` inside an attribute value (e.g. `data={[{ ... }]}` or
104367
- // `data={{ a: { b: 1 } }}`) must inherit the same exemption; only the
104368
- // outer `{` is directly after `=`.
104369
- if (!isAttrExpr && openStack.length > 0 && openStack[openStack.length - 1].isAttrExpr) {
104370
- isAttrExpr = true;
104371
- }
104372
- openStack.push({ pos: i, hasBlankLine: false, isAttrExpr, isEsmDeclaration: insideEsmDeclaration });
104373
- lastNewlinePos = -2;
104374
- }
104375
- else if (ch === '}') {
104376
- if (openStack.length > 0) {
104377
- const entry = openStack.pop();
104378
- // The declaration's braces are closed; later top-level braces are not ESM.
104379
- if (openStack.length === 0)
104380
- insideEsmDeclaration = false;
104381
- // Pure `{/* ... */}` comments are handled downstream by the jsxComment
104382
- // tokenizer — escaping their braces would prevent it from running.
104383
- const isPureJsxComment = chars[entry.pos + 1] === '/' &&
104384
- chars[entry.pos + 2] === '*' &&
104385
- chars[i - 1] === '/' &&
104386
- chars[i - 2] === '*';
104387
- if (entry.hasBlankLine && !isPureJsxComment && !entry.isAttrExpr && !entry.isEsmDeclaration) {
104388
- toEscape.add(entry.pos);
104389
- toEscape.add(i);
104390
- }
104391
- }
104392
- else {
104393
- toEscape.add(i);
104394
- }
104395
- }
104396
- else if (ch === ';' && openStack.length === 0) {
104397
- // A top-level `;` ends the current statement, ESM or otherwise.
104398
- insideEsmDeclaration = false;
104399
- }
104400
- }
104401
- // Anything still open is unbalanced.
104402
- openStack.forEach(entry => {
104403
- if (!entry.isEsmDeclaration)
104404
- toEscape.add(entry.pos);
104405
- });
104406
- // Reconstruct the content with the escaped braces.
104407
- const escapedContent = toEscape.size === 0 ? protectedContent : chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
104408
- return restoreHTMLElements(escapedContent, htmlElements);
104409
- }
104410
- /**
104411
- * Preprocesses JSX-like markdown content before parsing.
104412
- *
104413
- * JSX attribute expressions (`href={baseUrl}`) are no longer rewritten here —
104414
- * they flow through the tokenizer as `mdxJsxAttributeValueExpression` nodes
104415
- * and are evaluated at the hast handler step.
104416
- *
104417
- * @param content
104418
- * @returns Preprocessed content ready for markdown parsing
104419
- */
104420
- function preprocessJSXExpressions(content) {
104421
- const { protectedCode, protectedContent } = protectCodeBlocks(content);
104422
- let processed = escapeProblematicBraces(protectedContent);
104423
- processed = restoreCodeBlocks(processed, protectedCode);
104424
- return processed;
104425
- }
104426
104288
 
104427
104289
  ;// ./processor/transform/mdxish/resolve-deferred-attribute-expression-props.ts
104428
104290
 
@@ -105397,6 +105259,85 @@ function jsxTable() {
105397
105259
  ;// ./lib/micromark/jsx-table/index.ts
105398
105260
 
105399
105261
 
105262
+ ;// ./lib/micromark/mdx-expression-lenient/syntax.ts
105263
+
105264
+
105265
+ /**
105266
+ * Lenient MDX text-expression tokenizer (agnostic / no acorn).
105267
+ *
105268
+ * Matches a balanced `{ ... }` run — tracking nested braces and spanning soft
105269
+ * line breaks — and emits the standard `mdxTextExpression*` tokens so the
105270
+ * upstream `mdxExpressionFromMarkdown()` builds the node.
105271
+ *
105272
+ * Reimplements the official micromark mdxExpression, but an unbalanced brace that
105273
+ * reaches end of input returns `nok` instead of throwing: micromark rolls back
105274
+ * and the `{` renders as literal text, making the pipeline forgiving of stray
105275
+ * braces that upstream would hard-error on.
105276
+ */
105277
+ function tokenizeTextExpression(effects, ok, nok) {
105278
+ let depth = 0;
105279
+ return start;
105280
+ function start(code) {
105281
+ if (code !== codes.leftCurlyBrace)
105282
+ return nok(code);
105283
+ effects.enter('mdxTextExpression');
105284
+ effects.enter('mdxTextExpressionMarker');
105285
+ effects.consume(code);
105286
+ effects.exit('mdxTextExpressionMarker');
105287
+ return before;
105288
+ }
105289
+ function before(code) {
105290
+ if (code === codes.eof) {
105291
+ effects.exit('mdxTextExpression');
105292
+ return nok(code);
105293
+ }
105294
+ if (markdownLineEnding(code)) {
105295
+ effects.enter('lineEnding');
105296
+ effects.consume(code);
105297
+ effects.exit('lineEnding');
105298
+ return before;
105299
+ }
105300
+ if (code === codes.rightCurlyBrace && depth === 0) {
105301
+ return close(code);
105302
+ }
105303
+ effects.enter('mdxTextExpressionChunk');
105304
+ return inside(code);
105305
+ }
105306
+ function inside(code) {
105307
+ if (code === codes.eof || markdownLineEnding(code)) {
105308
+ effects.exit('mdxTextExpressionChunk');
105309
+ return before(code);
105310
+ }
105311
+ if (code === codes.rightCurlyBrace && depth === 0) {
105312
+ effects.exit('mdxTextExpressionChunk');
105313
+ return close(code);
105314
+ }
105315
+ if (code === codes.leftCurlyBrace)
105316
+ depth += 1;
105317
+ else if (code === codes.rightCurlyBrace)
105318
+ depth -= 1;
105319
+ effects.consume(code);
105320
+ return inside;
105321
+ }
105322
+ function close(code) {
105323
+ effects.enter('mdxTextExpressionMarker');
105324
+ effects.consume(code);
105325
+ effects.exit('mdxTextExpressionMarker');
105326
+ effects.exit('mdxTextExpression');
105327
+ return ok;
105328
+ }
105329
+ }
105330
+ function mdxExpressionLenient() {
105331
+ return {
105332
+ text: {
105333
+ [codes.leftCurlyBrace]: {
105334
+ name: 'mdxTextExpression',
105335
+ tokenize: tokenizeTextExpression,
105336
+ },
105337
+ },
105338
+ };
105339
+ }
105340
+
105400
105341
  ;// ./lib/utils/mdxish/mdxish-load-components.ts
105401
105342
 
105402
105343
 
@@ -105509,7 +105450,7 @@ const defaultTransformers = [
105509
105450
  * 1. Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
105510
105451
  * 2. Terminate HTML flow blocks so subsequent content isn't swallowed
105511
105452
  * 3. Close invalid "self-closing" HTML tags (e.g., `<i />` → `<i></i>`)
105512
- * 4. Escape problematic braces so MDX expression parsing doesn't choke
105453
+ * 4. Normalize compact ATX headings (e.g., `#Heading` `# Heading`)
105513
105454
  * 5. Replace snake_case component names with parser-safe placeholders
105514
105455
  */
105515
105456
  function preprocessContent(content, opts) {
@@ -105518,7 +105459,6 @@ function preprocessContent(content, opts) {
105518
105459
  result = terminateHtmlFlowBlocks(result);
105519
105460
  result = closeSelfClosingHtmlTags(result);
105520
105461
  result = normalizeCompactHeadings(result);
105521
- result = preprocessJSXExpressions(result);
105522
105462
  return processSnakeCaseComponent(result, { knownComponents });
105523
105463
  }
105524
105464
  function mdxishAstProcessor(mdContent, opts = {}) {
@@ -105535,12 +105475,8 @@ function mdxishAstProcessor(mdContent, opts = {}) {
105535
105475
  acc[key] = String(value);
105536
105476
  return acc;
105537
105477
  }, {});
105538
- // Get mdxExpression extension and remove its flow construct to prevent
105539
- // `{...}` from interrupting paragraphs (which breaks multiline magic blocks)
105540
- const mdxExprExt = mdxExpression({ allowEmpty: true });
105541
- const mdxExprTextOnly = {
105542
- text: mdxExprExt.text,
105543
- };
105478
+ // Parser extension for MDX expressions {}
105479
+ const mdxExprTextOnly = mdxExpressionLenient();
105544
105480
  const micromarkExts = [
105545
105481
  jsxTable(),
105546
105482
  magicBlock(),