Foo
- -## Community help (free) -### GitHub -### Unofficial 👍 -### Warning ⚠️ -### Header with Pro plan -### Header with \`code\` -`; - - const { - docs: { - en: { toc }, - }, - } = prepareMarkdown({ - ...defaultParams, - translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], - }); - - expect(toc).to.have.deep.ordered.members([ - { - children: [ - { - hash: 'github', - level: 3, - text: 'GitHub ', - }, - { hash: 'unofficial', level: 3, text: 'Unofficial 👍' }, - { hash: 'warning', level: 3, text: 'Warning ⚠️' }, - { - hash: 'header-with-pro-plan', - level: 3, - text: 'Header with Pro plan ', - }, - { - hash: 'header-with-code', - level: 3, - text: 'Header with code', - }, - ], - hash: 'community-help-free', - level: 2, - text: 'Community help (free)', - }, - ]); - }); - - it('enables word-break for function signatures', () => { - const markdown = ` -# Theming - -Foo
- -## API -### responsiveFontSizes(theme, options) => theme -### createTheme(options, ...args) => theme -`; - - const { - docs: { - en: { toc }, - }, - } = prepareMarkdown({ - ...defaultParams, - translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], - }); - - expect(toc).to.have.deep.ordered.members([ - { - children: [ - { - hash: 'responsivefontsizes-theme-options-theme', - level: 3, - text: 'responsiveFontSizes(theme, options) => theme', - }, - { - hash: 'createtheme-options-args-theme', - level: 3, - text: 'createTheme(options, ...args) => theme', - }, - ], - hash: 'api', - level: 2, - text: 'API', - }, - ]); - }); - - it('use english hash for different locales', () => { - const markdownEn = ` -# Localization - -Foo
- -## Locales -### Example -### Use same hash -`; - - const markdownPt = ` -# Localização - -Foo
- -## Idiomas -### Exemplo -### Usar o mesmo hash -`; - - const markdownZh = ` -# 所在位置 - -Foo
- -## 语言环境 -### 例 -### 使用相同的哈希 -`; - const { - docs: { - en: { toc: tocEn }, - pt: { toc: tocPt }, - zh: { toc: tocZh }, - }, - } = prepareMarkdown({ - pageFilename: '/same-hash-test', - translations: [ - { filename: 'localization.md', markdown: markdownEn, userLanguage: 'en' }, - { filename: 'localization-pt.md', markdown: markdownPt, userLanguage: 'pt' }, - { filename: 'localization-zh.md', markdown: markdownZh, userLanguage: 'zh' }, - ], - }); - - expect(tocZh).to.have.deep.ordered.members([ - { - children: [ - { - hash: 'example', - level: 3, - text: '例', - }, - { - hash: 'use-same-hash', - level: 3, - text: '使用相同的哈希', - }, - ], - hash: 'locales', - level: 2, - text: '语言环境', - }, - ]); - - expect(tocPt).to.have.deep.ordered.members([ - { - children: [ - { - hash: 'example', - level: 3, - text: 'Exemplo', - }, - { - hash: 'use-same-hash', - level: 3, - text: 'Usar o mesmo hash', - }, - ], - hash: 'locales', - level: 2, - text: 'Idiomas', - }, - ]); - - expect(tocEn).to.have.deep.ordered.members([ - { - children: [ - { - hash: 'example', - level: 3, - text: 'Example', - }, - { - hash: 'use-same-hash', - level: 3, - text: 'Use same hash', - }, - ], - hash: 'locales', - level: 2, - text: 'Locales', - }, - ]); - }); - - it('use translated hash for translations are not synced', () => { - const markdownEn = ` -# Localization - -Foo
- -## Locales -### Example -### Use same hash -`; - - const markdownPt = ` -# Localização - -Foo
- -## Idiomas -### Exemplo -### Usar o mesmo hash -### Usar traduzido -`; - - const { - docs: { - en: { toc: tocEn }, - pt: { toc: tocPt }, - }, - } = prepareMarkdown({ - pageFilename: '/same-hash-test', - translations: [ - { filename: 'localization.md', markdown: markdownEn, userLanguage: 'en' }, - { filename: 'localization-pt.md', markdown: markdownPt, userLanguage: 'pt' }, - ], - }); - - expect(tocPt).to.have.deep.ordered.members([ - { - children: [ - { - hash: 'example', - level: 3, - text: 'Exemplo', - }, - { - hash: 'use-same-hash', - level: 3, - text: 'Usar o mesmo hash', - }, - { - hash: 'usar-traduzido', - level: 3, - text: 'Usar traduzido', - }, - ], - hash: 'locales', - level: 2, - text: 'Idiomas', - }, - ]); - - expect(tocEn).to.have.deep.ordered.members([ - { - children: [ - { - hash: 'example', - level: 3, - text: 'Example', - }, - { - hash: 'use-same-hash', - level: 3, - text: 'Use same hash', - }, - ], - hash: 'locales', - level: 2, - text: 'Locales', - }, - ]); - }); - - it('should report missing trailing splashes', () => { - const markdown = ` -# Localization - -Foo
- -[bar](/bar/) -[foo](/foo) -`; - - expect(() => { - prepareMarkdown({ - ...defaultParams, - translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], - }); - }).to.throw(`docs-infra: Missing trailing slash. The following link: -[foo](/foo) in /test/bar/index.md is missing a trailing slash, please add it. - -See https://ahrefs.com/blog/trailing-slash/ for more details. -`); - }); - - it('should report missing leading splashes', () => { - const markdown = ` -# Localization - -Foo
- -[bar](/bar/) -[foo](foo/) -`; - - expect(() => { - prepareMarkdown({ - ...defaultParams, - translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], - }); - }).to.throw(`docs-infra: Missing leading slash. The following link: -[foo](foo/) in /test/bar/index.md is missing a leading slash, please add it. -`); - }); - - it('should report title too long', () => { - const markdown = ` -# Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo - -Foo
- -`; - - expect(() => { - prepareMarkdown({ - ...defaultParams, - translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], - }); - }).to - .throw(`docs-infra: The title "Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" is too long (117 characters). -It needs to have fewer than 70 characters—ideally less than 60. For more details, see: -https://developers.google.com/search/docs/advanced/appearance/title-link -`); - }); - - it('should report description too long', () => { - const markdown = ` -# Foo - -Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
- -`; - - expect(() => { - prepareMarkdown({ - ...defaultParams, - translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], - }); - }).to - .throw(`docs-infra: The description "Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" is too long (188 characters). -It needs to have fewer than 170 characters—ideally less than 160. For more details, see: -https://ahrefs.com/blog/meta-description/#4-be-concise -`); - }); - }); - describe('getCodeblock', () => { it('should return undefined if no codeblock found', () => { const codeblock = getCodeblock('## Tabs'); @@ -611,31 +234,4 @@ https://ahrefs.com/blog/meta-description/#4-be-concise }); }); }); - - it('should not accept sh', () => { - const markdown = ` -# Foo - -Fo
- -\`\`\`sh -npm install @mui/material -\`\`\` - -`; - - expect(() => { - prepareMarkdown({ - ...defaultParams, - translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], - }); - }).to.throw(`docs-infra: Unsupported language: "sh" in: - -\`\`\`sh -npm install @mui/material -\`\`\` - -Use "bash" instead. -`); - }); }); diff --git a/packages/markdown/prepareMarkdown.js b/packages/markdown/prepareMarkdown.js new file mode 100644 index 00000000000000..6447e88dfdfaa9 --- /dev/null +++ b/packages/markdown/prepareMarkdown.js @@ -0,0 +1,259 @@ +const fs = require('fs'); +const path = require('path'); +const kebabCase = require('lodash/kebabCase'); +const { + createRender, + getContents, + getDescription, + getCodeblock, + getHeaders, + getTitle, +} = require('./parseMarkdown'); + +const BaseUIReexportedComponents = ['ClickAwayListener', 'NoSsr', 'Portal', 'TextareaAutosize']; + +/** + * @param {string} productId + * @example 'material' + * @param {string} componentPkg + * @example 'mui-base' + * @param {string} component + * @example 'Button' + * @returns {string} + */ +function resolveComponentApiUrl(productId, componentPkg, component) { + if (!productId) { + return `/api/${kebabCase(component)}/`; + } + if (productId === 'x-date-pickers') { + return `/x/api/date-pickers/${kebabCase(component)}/`; + } + if (productId === 'x-charts') { + return `/x/api/charts/${kebabCase(component)}/`; + } + if (productId === 'x-tree-view') { + return `/x/api/tree-view/${kebabCase(component)}/`; + } + if (componentPkg === 'mui-base' || BaseUIReexportedComponents.indexOf(component) >= 0) { + return `/base-ui/react-${kebabCase(component)}/components-api/#${kebabCase(component)}`; + } + return `/${productId}/api/${kebabCase(component)}/`; +} + +/** + * @param {object} config + * @param {Array<{ markdown: string, filename: string, userLanguage: string }>} config.translations - Mapping of locale to its markdown + * @param {string} config.fileRelativeContext - posix filename relative to repository root directory + * @param {object} config.options - provided to the webpack loader + */ +function prepareMarkdown(config) { + const { fileRelativeContext, translations, componentPackageMapping = {}, options } = config; + + const demos = {}; + /** + * @type {RecordFoo
+ +## Community help (free) +### GitHub +### Unofficial 👍 +### Warning ⚠️ +### Header with Pro plan +### Header with \`code\` +`; + + const { + docs: { + en: { toc }, + }, + } = prepareMarkdown({ + ...defaultParams, + translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], + }); + + expect(toc).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'github', + level: 3, + text: 'GitHub ', + }, + { hash: 'unofficial', level: 3, text: 'Unofficial 👍' }, + { hash: 'warning', level: 3, text: 'Warning ⚠️' }, + { + hash: 'header-with-pro-plan', + level: 3, + text: 'Header with Pro plan ', + }, + { + hash: 'header-with-code', + level: 3, + text: 'Header with code', + }, + ], + hash: 'community-help-free', + level: 2, + text: 'Community help (free)', + }, + ]); + }); + + it('enables word-break for function signatures', () => { + const markdown = ` +# Theming + +Foo
+ +## API +### responsiveFontSizes(theme, options) => theme +### createTheme(options, ...args) => theme +`; + + const { + docs: { + en: { toc }, + }, + } = prepareMarkdown({ + ...defaultParams, + translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], + }); + + expect(toc).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'responsivefontsizes-theme-options-theme', + level: 3, + text: 'responsiveFontSizes(theme, options) => theme', + }, + { + hash: 'createtheme-options-args-theme', + level: 3, + text: 'createTheme(options, ...args) => theme', + }, + ], + hash: 'api', + level: 2, + text: 'API', + }, + ]); + }); + + it('use english hash for different locales', () => { + const markdownEn = ` +# Localization + +Foo
+ +## Locales +### Example +### Use same hash +`; + + const markdownPt = ` +# Localização + +Foo
+ +## Idiomas +### Exemplo +### Usar o mesmo hash +`; + + const markdownZh = ` +# 所在位置 + +Foo
+ +## 语言环境 +### 例 +### 使用相同的哈希 +`; + const { + docs: { + en: { toc: tocEn }, + pt: { toc: tocPt }, + zh: { toc: tocZh }, + }, + } = prepareMarkdown({ + pageFilename: '/same-hash-test', + translations: [ + { filename: 'localization.md', markdown: markdownEn, userLanguage: 'en' }, + { filename: 'localization-pt.md', markdown: markdownPt, userLanguage: 'pt' }, + { filename: 'localization-zh.md', markdown: markdownZh, userLanguage: 'zh' }, + ], + }); + + expect(tocZh).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: '例', + }, + { + hash: 'use-same-hash', + level: 3, + text: '使用相同的哈希', + }, + ], + hash: 'locales', + level: 2, + text: '语言环境', + }, + ]); + + expect(tocPt).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: 'Exemplo', + }, + { + hash: 'use-same-hash', + level: 3, + text: 'Usar o mesmo hash', + }, + ], + hash: 'locales', + level: 2, + text: 'Idiomas', + }, + ]); + + expect(tocEn).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: 'Example', + }, + { + hash: 'use-same-hash', + level: 3, + text: 'Use same hash', + }, + ], + hash: 'locales', + level: 2, + text: 'Locales', + }, + ]); + }); + + it('use translated hash for translations are not synced', () => { + const markdownEn = ` +# Localization + +Foo
+ +## Locales +### Example +### Use same hash +`; + + const markdownPt = ` +# Localização + +Foo
+ +## Idiomas +### Exemplo +### Usar o mesmo hash +### Usar traduzido +`; + + const { + docs: { + en: { toc: tocEn }, + pt: { toc: tocPt }, + }, + } = prepareMarkdown({ + pageFilename: '/same-hash-test', + translations: [ + { filename: 'localization.md', markdown: markdownEn, userLanguage: 'en' }, + { filename: 'localization-pt.md', markdown: markdownPt, userLanguage: 'pt' }, + ], + }); + + expect(tocPt).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: 'Exemplo', + }, + { + hash: 'use-same-hash', + level: 3, + text: 'Usar o mesmo hash', + }, + { + hash: 'usar-traduzido', + level: 3, + text: 'Usar traduzido', + }, + ], + hash: 'locales', + level: 2, + text: 'Idiomas', + }, + ]); + + expect(tocEn).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: 'Example', + }, + { + hash: 'use-same-hash', + level: 3, + text: 'Use same hash', + }, + ], + hash: 'locales', + level: 2, + text: 'Locales', + }, + ]); + }); + + it('should report missing trailing splashes', () => { + const markdown = ` +# Localization + +Foo
+ +[bar](/bar/) +[foo](/foo) +`; + + expect(() => { + prepareMarkdown({ + ...defaultParams, + translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], + }); + }).to.throw(`docs-infra: Missing trailing slash. The following link: +[foo](/foo) in /test/bar/index.md is missing a trailing slash, please add it. + +See https://ahrefs.com/blog/trailing-slash/ for more details. +`); + }); + + it('should report missing leading splashes', () => { + const markdown = ` +# Localization + +Foo
+ +[bar](/bar/) +[foo](foo/) +`; + + expect(() => { + prepareMarkdown({ + ...defaultParams, + translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], + }); + }).to.throw(`docs-infra: Missing leading slash. The following link: +[foo](foo/) in /test/bar/index.md is missing a leading slash, please add it. +`); + }); + + it('should report title too long', () => { + const markdown = ` +# Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo + +Foo
+ +`; + + expect(() => { + prepareMarkdown({ + ...defaultParams, + translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], + }); + }).to + .throw(`docs-infra: The title "Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" is too long (117 characters). +It needs to have fewer than 70 characters—ideally less than 60. For more details, see: +https://developers.google.com/search/docs/advanced/appearance/title-link +`); + }); + + it('should report description too long', () => { + const markdown = ` +# Foo + +Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ +`; + + expect(() => { + prepareMarkdown({ + ...defaultParams, + translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], + }); + }).to + .throw(`docs-infra: The description "Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" is too long (188 characters). +It needs to have fewer than 170 characters—ideally less than 160. For more details, see: +https://ahrefs.com/blog/meta-description/#4-be-concise +`); + }); + + it('should not accept sh', () => { + const markdown = ` +# Foo + +Fo
+ +\`\`\`sh +npm install @mui/material +\`\`\` + +`; + + expect(() => { + prepareMarkdown({ + ...defaultParams, + translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }], + }); + }).to.throw(`docs-infra: Unsupported language: "sh" in: + +\`\`\`sh +npm install @mui/material +\`\`\` + +Use "bash" instead. +`); + }); +});