diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index f7adaffcf413..e773922bc21b 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -15,4 +15,4 @@ jobs: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Dependency Review - uses: actions/dependency-review-action@7bbfa034e752445ea40215fff1c3bf9597993d3f # 3.1.3 + uses: actions/dependency-review-action@01bc87099ba56df1e897b6874784491ea6309bc4 # 3.1.4 diff --git a/.github/workflows/lint-autofix.yml b/.github/workflows/lint-autofix.yml index dd5bc847cdfd..ed857a3e9130 100644 --- a/.github/workflows/lint-autofix.yml +++ b/.github/workflows/lint-autofix.yml @@ -21,6 +21,7 @@ jobs: steps: - uses: actions/checkout@v4 with: + repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.head_ref }} - name: Installation diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index 09c3f2fb3d59..1f0e7679420a 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -356,7 +356,9 @@ declare module '@docusaurus/useGlobalData' { declare module '*.svg' { import type {ComponentType, SVGProps} from 'react'; - const ReactComponent: ComponentType>; + const ReactComponent: ComponentType< + SVGProps & {title?: string} + >; export default ReactComponent; } diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index 19515459a656..052437c3dec6 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -19,6 +19,7 @@ describe('getBlogPostAuthors', () => { getBlogPostAuthors({ frontMatter: {}, authorsMap: undefined, + baseUrl: '/', }), ).toEqual([]); expect( @@ -27,6 +28,7 @@ describe('getBlogPostAuthors', () => { authors: [], }, authorsMap: undefined, + baseUrl: '/', }), ).toEqual([]); }); @@ -38,6 +40,7 @@ describe('getBlogPostAuthors', () => { author: 'Sébastien Lorber', }, authorsMap: undefined, + baseUrl: '/', }), ).toEqual([{name: 'Sébastien Lorber'}]); expect( @@ -46,6 +49,7 @@ describe('getBlogPostAuthors', () => { authorTitle: 'maintainer', }, authorsMap: undefined, + baseUrl: '/', }), ).toEqual([{title: 'maintainer'}]); expect( @@ -54,8 +58,27 @@ describe('getBlogPostAuthors', () => { authorImageURL: 'https://github.com/slorber.png', }, authorsMap: undefined, + baseUrl: '/', }), ).toEqual([{imageURL: 'https://github.com/slorber.png'}]); + expect( + getBlogPostAuthors({ + frontMatter: { + authorImageURL: '/img/slorber.png', + }, + authorsMap: undefined, + baseUrl: '/', + }), + ).toEqual([{imageURL: '/img/slorber.png'}]); + expect( + getBlogPostAuthors({ + frontMatter: { + authorImageURL: '/img/slorber.png', + }, + authorsMap: undefined, + baseUrl: '/baseURL', + }), + ).toEqual([{imageURL: '/baseURL/img/slorber.png'}]); expect( getBlogPostAuthors({ frontMatter: { @@ -68,6 +91,7 @@ describe('getBlogPostAuthors', () => { authorURL: 'https://github.com/slorber2', }, authorsMap: undefined, + baseUrl: '/', }), ).toEqual([ { @@ -86,8 +110,69 @@ describe('getBlogPostAuthors', () => { authors: 'slorber', }, authorsMap: {slorber: {name: 'Sébastien Lorber'}}, + baseUrl: '/', }), ).toEqual([{key: 'slorber', name: 'Sébastien Lorber'}]); + expect( + getBlogPostAuthors({ + frontMatter: { + authors: 'slorber', + }, + authorsMap: { + slorber: { + name: 'Sébastien Lorber', + imageURL: 'https://github.com/slorber.png', + }, + }, + baseUrl: '/', + }), + ).toEqual([ + { + key: 'slorber', + name: 'Sébastien Lorber', + imageURL: 'https://github.com/slorber.png', + }, + ]); + expect( + getBlogPostAuthors({ + frontMatter: { + authors: 'slorber', + }, + authorsMap: { + slorber: { + name: 'Sébastien Lorber', + imageURL: '/img/slorber.png', + }, + }, + baseUrl: '/', + }), + ).toEqual([ + { + key: 'slorber', + name: 'Sébastien Lorber', + imageURL: '/img/slorber.png', + }, + ]); + expect( + getBlogPostAuthors({ + frontMatter: { + authors: 'slorber', + }, + authorsMap: { + slorber: { + name: 'Sébastien Lorber', + imageURL: '/img/slorber.png', + }, + }, + baseUrl: '/baseUrl', + }), + ).toEqual([ + { + key: 'slorber', + name: 'Sébastien Lorber', + imageURL: '/baseUrl/img/slorber.png', + }, + ]); }); it('can read authors string[]', () => { @@ -100,6 +185,7 @@ describe('getBlogPostAuthors', () => { slorber: {name: 'Sébastien Lorber', title: 'maintainer'}, yangshun: {name: 'Yangshun Tay'}, }, + baseUrl: '/', }), ).toEqual([ {key: 'slorber', name: 'Sébastien Lorber', title: 'maintainer'}, @@ -114,6 +200,7 @@ describe('getBlogPostAuthors', () => { authors: {name: 'Sébastien Lorber', title: 'maintainer'}, }, authorsMap: undefined, + baseUrl: '/', }), ).toEqual([{name: 'Sébastien Lorber', title: 'maintainer'}]); }); @@ -128,6 +215,7 @@ describe('getBlogPostAuthors', () => { ], }, authorsMap: undefined, + baseUrl: '/', }), ).toEqual([ {name: 'Sébastien Lorber', title: 'maintainer'}, @@ -153,6 +241,7 @@ describe('getBlogPostAuthors', () => { slorber: {name: 'Sébastien Lorber', title: 'maintainer'}, yangshun: {name: 'Yangshun Tay', title: 'Yangshun title original'}, }, + baseUrl: '/', }), ).toEqual([ {key: 'slorber', name: 'Sébastien Lorber', title: 'maintainer'}, @@ -173,6 +262,7 @@ describe('getBlogPostAuthors', () => { authors: 'slorber', }, authorsMap: undefined, + baseUrl: '/', }), ).toThrowErrorMatchingInlineSnapshot(` "Can't reference blog post authors by a key (such as 'slorber') because no authors map file could be loaded. @@ -187,6 +277,7 @@ describe('getBlogPostAuthors', () => { authors: 'slorber', }, authorsMap: {}, + baseUrl: '/', }), ).toThrowErrorMatchingInlineSnapshot(` "Can't reference blog post authors by a key (such as 'slorber') because no authors map file could be loaded. @@ -205,6 +296,7 @@ describe('getBlogPostAuthors', () => { yangshun: {name: 'Yangshun Tay'}, jmarcey: {name: 'Joel Marcey'}, }, + baseUrl: '/', }), ).toThrowErrorMatchingInlineSnapshot(` "Blog author with key "slorber" not found in the authors map file. @@ -225,6 +317,7 @@ describe('getBlogPostAuthors', () => { yangshun: {name: 'Yangshun Tay'}, jmarcey: {name: 'Joel Marcey'}, }, + baseUrl: '/', }), ).toThrowErrorMatchingInlineSnapshot(` "Blog author with key "slorber" not found in the authors map file. @@ -245,6 +338,7 @@ describe('getBlogPostAuthors', () => { yangshun: {name: 'Yangshun Tay'}, jmarcey: {name: 'Joel Marcey'}, }, + baseUrl: '/', }), ).toThrowErrorMatchingInlineSnapshot(` "Blog author with key "slorber" not found in the authors map file. @@ -262,6 +356,7 @@ describe('getBlogPostAuthors', () => { author: 'Yangshun Tay', }, authorsMap: undefined, + baseUrl: '/', }), ).toThrowErrorMatchingInlineSnapshot(` "To declare blog post authors, use the 'authors' front matter in priority. @@ -275,6 +370,7 @@ describe('getBlogPostAuthors', () => { author_title: 'legacy title', }, authorsMap: {slorber: {name: 'Sébastien Lorber'}}, + baseUrl: '/', }), ).toThrowErrorMatchingInlineSnapshot(` "To declare blog post authors, use the 'authors' front matter in priority. diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 4d8f7b90b3fb..c5fdad61ddfb 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {getDataFileData} from '@docusaurus/utils'; +import {getDataFileData, normalizeUrl} from '@docusaurus/utils'; import {Joi, URISchema} from '@docusaurus/utils-validation'; import type {BlogContentPaths} from './types'; import type { @@ -68,17 +68,37 @@ export async function getAuthorsMap(params: { type AuthorsParam = { frontMatter: BlogPostFrontMatter; authorsMap: AuthorsMap | undefined; + baseUrl: string; }; +function normalizeImageUrl({ + imageURL, + baseUrl, +}: { + imageURL: string | undefined; + baseUrl: string; +}) { + return imageURL?.startsWith('/') + ? normalizeUrl([baseUrl, imageURL]) + : imageURL; +} + // Legacy v1/early-v2 front matter fields // We may want to deprecate those in favor of using only frontMatter.authors -function getFrontMatterAuthorLegacy( - frontMatter: BlogPostFrontMatter, -): Author | undefined { +function getFrontMatterAuthorLegacy({ + baseUrl, + frontMatter, +}: { + baseUrl: string; + frontMatter: BlogPostFrontMatter; +}): Author | undefined { const name = frontMatter.author; const title = frontMatter.author_title ?? frontMatter.authorTitle; const url = frontMatter.author_url ?? frontMatter.authorURL; - const imageURL = frontMatter.author_image_url ?? frontMatter.authorImageURL; + const imageURL = normalizeImageUrl({ + imageURL: frontMatter.author_image_url ?? frontMatter.authorImageURL, + baseUrl, + }); if (name || title || url || imageURL) { return { @@ -148,14 +168,26 @@ ${Object.keys(authorsMap) return frontMatterAuthors.map(toAuthor); } +function fixAuthorImageBaseURL( + authors: Author[], + {baseUrl}: {baseUrl: string}, +) { + return authors.map((author) => ({ + ...author, + imageURL: normalizeImageUrl({imageURL: author.imageURL, baseUrl}), + })); +} + export function getBlogPostAuthors(params: AuthorsParam): Author[] { - const authorLegacy = getFrontMatterAuthorLegacy(params.frontMatter); + const authorLegacy = getFrontMatterAuthorLegacy(params); const authors = getFrontMatterAuthors(params); + const updatedAuthors = fixAuthorImageBaseURL(authors, params); + if (authorLegacy) { // Technically, we could allow mixing legacy/authors front matter, but do we // really want to? - if (authors.length > 0) { + if (updatedAuthors.length > 0) { throw new Error( `To declare blog post authors, use the 'authors' front matter in priority. Don't mix 'authors' with other existing 'author_*' front matter. Choose one or the other, not both at the same time.`, @@ -164,5 +196,5 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t return [authorLegacy]; } - return authors; + return updatedAuthors; } diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 36028495381c..0a8d2a0e0b67 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -319,7 +319,7 @@ async function processBlogSourceFile( routeBasePath, tagsRouteBasePath, ]); - const authors = getBlogPostAuthors({authorsMap, frontMatter}); + const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl}); return { id: slug, diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx index ae0b2c40233e..a55915086532 100644 --- a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx @@ -98,6 +98,10 @@ function EditorWithHeader() { ); } +// this should rather be a stable function +// see https://github.com/facebook/docusaurus/issues/9630#issuecomment-1855682643 +const DEFAULT_TRANSFORM_CODE = (code: string) => `${code};`; + export default function Playground({ children, transformCode, @@ -118,7 +122,7 @@ export default function Playground({ `${code};`)} + transformCode={transformCode ?? DEFAULT_TRANSFORM_CODE} theme={prismTheme} {...props}> {playgroundPosition === 'top' ? ( diff --git a/packages/docusaurus-utils/src/__tests__/__snapshots__/markdownLinks.test.ts.snap b/packages/docusaurus-utils/src/__tests__/__snapshots__/markdownLinks.test.ts.snap index 7c5fc874287c..3c4f732a82fd 100644 --- a/packages/docusaurus-utils/src/__tests__/__snapshots__/markdownLinks.test.ts.snap +++ b/packages/docusaurus-utils/src/__tests__/__snapshots__/markdownLinks.test.ts.snap @@ -176,6 +176,7 @@ exports[`replaceMarkdownLinks replaces links with same title as URL 1`] = ` "brokenMarkdownLinks": [], "newContent": " [foo.md](/docs/foo) +[./foo.md]() [./foo.md](/docs/foo) [foo.md](/docs/foo) [./foo.md](/docs/foo) diff --git a/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts b/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts index b7d7abd556b5..ce0acfb3a99b 100644 --- a/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts +++ b/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts @@ -231,6 +231,7 @@ The following operations are defined for [URI]s: }, fileString: ` [foo.md](foo.md) +[./foo.md](<./foo.md>) [./foo.md](./foo.md) [foo.md](./foo.md) [./foo.md](foo.md) diff --git a/packages/docusaurus-utils/src/constants.ts b/packages/docusaurus-utils/src/constants.ts index 6ecba7aea0f5..5039d6989b00 100644 --- a/packages/docusaurus-utils/src/constants.ts +++ b/packages/docusaurus-utils/src/constants.ts @@ -83,7 +83,9 @@ export const DEFAULT_I18N_DIR_NAME = 'i18n'; export const CODE_TRANSLATIONS_FILE_NAME = 'code.json'; /** Dev server opens on this port by default. */ -export const DEFAULT_PORT = 3000; +export const DEFAULT_PORT = process.env.PORT + ? parseInt(process.env.PORT, 10) + : 3000; /** Default plugin ID. */ export const DEFAULT_PLUGIN_ID = 'default'; diff --git a/packages/docusaurus-utils/src/markdownLinks.ts b/packages/docusaurus-utils/src/markdownLinks.ts index 88825e502f14..13afca33905e 100644 --- a/packages/docusaurus-utils/src/markdownLinks.ts +++ b/packages/docusaurus-utils/src/markdownLinks.ts @@ -128,7 +128,7 @@ export function replaceMarkdownLinks({ const linkSuffixPattern = '(?:\\?[^#>\\s]+)?(?:#[^>\\s]+)?'; const linkCapture = (forbidden: string) => `((?!https?://|@site/)[^${forbidden}#?]+)`; - const linkURLPattern = `(?:${linkCapture( + const linkURLPattern = `(?:(?!<)${linkCapture( '()\\s', )}${linkSuffixPattern}|<${linkCapture('>')}${linkSuffixPattern}>)`; const linkPattern = new RegExp( diff --git a/project-words.txt b/project-words.txt index 2520099398e6..5bb1628e9788 100644 --- a/project-words.txt +++ b/project-words.txt @@ -5,6 +5,7 @@ Agan alexbdebrie Alexey algoliasearch +Allez Anshul anshul août @@ -185,6 +186,7 @@ Mdxjs mdxjs Meilisearch meilisearch +merveilleuse metadatum metastring metrica @@ -379,6 +381,7 @@ Vieira Viet Vinnik vjeux +voir waivable WCAG wcag diff --git a/website/docs/docusaurus-core.mdx b/website/docs/docusaurus-core.mdx index e96d9ccf5068..c14a53185e75 100644 --- a/website/docs/docusaurus-core.mdx +++ b/website/docs/docusaurus-core.mdx @@ -543,7 +543,7 @@ const MyComponent = () => { :::tip -Inspect your site's global data at `./docusaurus/globalData.json` +Inspect your site's global data at `.docusaurus/globalData.json` ::: diff --git a/website/docs/i18n/i18n-crowdin.mdx b/website/docs/i18n/i18n-crowdin.mdx index 6c215106f714..ef8c3808dd4d 100644 --- a/website/docs/i18n/i18n-crowdin.mdx +++ b/website/docs/i18n/i18n-crowdin.mdx @@ -515,3 +515,9 @@ import CodeBlock from '@theme/CodeBlock'; .join('\n')} ``` + +### Machine Translation (MT) issue: links/image handling + +Crowdin recently rolled out some major changes to the markdown file format and now the links are treated differently than they were before. Before they were considered as tags, but now they appear as plain text. Because of these changes the plain text links are passed to the MT engine which attempts to translate the target, thus breaking the translation (for instance: this string `Allez voir [ma merveilleuse page](/ma-merveilleuse-page)` is translated `Check out [my wonderful page](/my-wonderful-page)`, and this breaks docusaurus i18n workflow as the page name should not be translated). + +As of 2023 Dec.7, they are not planning to change the logic of how links are treated, so you should have this in mind if you plan to use Crowdin with MT.