diff --git a/plugins/remark-smartypants.js b/plugins/remark-smartypants.js index 7dd1b0c4a..4694ff674 100644 --- a/plugins/remark-smartypants.js +++ b/plugins/remark-smartypants.js @@ -1,3 +1,8 @@ +/*! + * Based on 'silvenon/remark-smartypants' + * https://github.com/silvenon/remark-smartypants/pull/80 + */ + const visit = require('unist-util-visit'); const retext = require('retext'); const smartypants = require('retext-smartypants'); @@ -9,12 +14,48 @@ function check(parent) { } module.exports = function (options) { - const processor = retext().use(smartypants, options); + const processor = retext().use(smartypants, { + ...options, + // Do not replace ellipses, dashes, backticks because they change string + // length, and we couldn't guarantee right splice of text in second visit of + // tree + ellipses: false, + dashes: false, + backticks: false, + }); + + const processor2 = retext().use(smartypants, { + ...options, + // Do not replace quotes because they are already replaced in the first + // processor + quotes: false, + }); function transformer(tree) { - visit(tree, 'text', (node, index, parent) => { - if (check(parent)) node.value = String(processor.processSync(node.value)); + let allText = ''; + let startIndex = 0; + const textOrInlineCodeNodes = []; + + visit(tree, ['text', 'inlineCode'], (node, _, parent) => { + if (check(parent)) { + if (node.type === 'text') allText += node.value; + // for the case when inlineCode contains just one part of quote: `foo'bar` + else allText += 'A'.repeat(node.value.length); + textOrInlineCodeNodes.push(node); + } }); + + // Concat all text into one string, to properly replace quotes around non-"text" nodes + allText = String(processor.processSync(allText)); + + for (const node of textOrInlineCodeNodes) { + const endIndex = startIndex + node.value.length; + if (node.type === 'text') { + const processedText = allText.slice(startIndex, endIndex); + node.value = String(processor2.processSync(processedText)); + } + startIndex = endIndex; + } } return transformer; diff --git a/src/components/Layout/HomeContent.js b/src/components/Layout/HomeContent.js index 93f2c0a4e..711e70a32 100644 --- a/src/components/Layout/HomeContent.js +++ b/src/components/Layout/HomeContent.js @@ -1507,7 +1507,7 @@ function ConferenceLayout({conf, children}) { navigate(e.target.value); }); }} - className="appearance-none pe-8 bg-transparent text-primary-dark text-2xl font-bold mb-0.5" + className="appearance-none pe-8 ps-2 bg-transparent text-primary-dark text-2xl font-bold mb-0.5" style={{ backgroundSize: '4px 4px, 4px 4px', backgroundRepeat: 'no-repeat', @@ -1516,8 +1516,16 @@ function ConferenceLayout({conf, children}) { backgroundImage: 'linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%)', }}> - - + +
diff --git a/src/components/MDX/Sandpack/Preview.tsx b/src/components/MDX/Sandpack/Preview.tsx index 059645550..9669e5f4f 100644 --- a/src/components/MDX/Sandpack/Preview.tsx +++ b/src/components/MDX/Sandpack/Preview.tsx @@ -54,7 +54,7 @@ export function Preview({ // When throwing a new Error in Sandpack - we want to disable the dev error dialog // to show the Error Boundary fallback - if (rawError && rawError.message.includes(`throw Error('Example error')`)) { + if (rawError && rawError.message.includes('Example Error:')) { rawError = null; } diff --git a/src/components/Seo.tsx b/src/components/Seo.tsx index 5af169e13..dfc4f6104 100644 --- a/src/components/Seo.tsx +++ b/src/components/Seo.tsx @@ -24,6 +24,7 @@ const deployedTranslations = [ 'es', 'fr', 'ja', + 'tr', // We'll add more languages when they have enough content. // Please DO NOT edit this list without a discussion in the reactjs/react.dev repo. // It must be the same between all translations. diff --git a/src/content/learn/understanding-your-ui-as-a-tree.md b/src/content/learn/understanding-your-ui-as-a-tree.md index f4528b346..2abf7affc 100644 --- a/src/content/learn/understanding-your-ui-as-a-tree.md +++ b/src/content/learn/understanding-your-ui-as-a-tree.md @@ -22,7 +22,7 @@ React, and many other UI libraries, model UI as a tree. Thinking of your app as Trees are a relationship model between items and UI is often represented using tree structures. For example, browsers use tree structures to model HTML ([DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction)) and CSS ([CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model)). Mobile platforms also use trees to represent their view hierarchy. - + React creates a UI tree from your components. In this example, the UI tree is then used to render to the DOM. diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md index 11719112e..610742735 100644 --- a/src/content/reference/react-dom/components/common.md +++ b/src/content/reference/react-dom/components/common.md @@ -982,6 +982,8 @@ textarea { display: block; margin-top: 5px; margin-bottom: 10px; } +The `{__html}` object should be created as close to where the HTML is generated as possible, like the above example does in the `renderMarkdownToHTML` function. This ensures that all raw HTML being used in your code is explicitly marked as such, and that only variables that you expect to contain HTML are passed to `dangerouslySetInnerHTML`. It is not recommended to create the object inline like `
`. + To see why rendering arbitrary HTML is dangerous, replace the code above with this: ```js {1-4,7,8} diff --git a/src/content/reference/react/use-server.md b/src/content/reference/react/use-server.md index 638163369..0d0fc8404 100644 --- a/src/content/reference/react/use-server.md +++ b/src/content/reference/react/use-server.md @@ -153,7 +153,7 @@ export default async function requestUsername(formData) { // UsernameForm.js 'use client'; -import {useFormState} from 'react-dom'; +import { useFormState } from 'react-dom'; import requestUsername from './requestUsername'; function UsernameForm() { @@ -208,7 +208,7 @@ function LikeButton() { 'use server'; let likeCount = 0; -export default async incrementLike() { +export default async function incrementLike() { likeCount++; return likeCount; } diff --git a/src/content/reference/react/useTransition.md b/src/content/reference/react/useTransition.md index 37e89c0c9..49df279fb 100644 --- a/src/content/reference/react/useTransition.md +++ b/src/content/reference/react/useTransition.md @@ -1520,15 +1520,15 @@ import { ErrorBoundary } from "react-error-boundary"; export function AddCommentContainer() { return ( ⚠️Something went wrong

}> - +
); } function addComment(comment) { // For demonstration purposes to show Error Boundary - if(comment == null){ - throw Error('Example error') + if (comment == null) { + throw new Error("Example Error: An error thrown to trigger error boundary"); } } @@ -1544,9 +1544,10 @@ function AddCommentButton() { // so error gets thrown addComment(); }); - }}> - Add comment - + }} + > + Add comment + ); } ``` diff --git a/src/utils/prepareMDX.js b/src/utils/prepareMDX.js index 6340c3b7f..677293ecd 100644 --- a/src/utils/prepareMDX.js +++ b/src/utils/prepareMDX.js @@ -7,7 +7,7 @@ import {Children} from 'react'; // TODO: This logic could be in MDX plugins instead. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -export const PREPARE_MDX_CACHE_BREAKER = 2; +export const PREPARE_MDX_CACHE_BREAKER = 3; // !!! IMPORTANT !!! Bump this if you change any logic. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~