/** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** @typedef {'Types'|'ReleaseNotesMd'} OutputType */ // @ts-check const toKebabCase = require('lodash/kebabCase.js') const Documentation = require('./documentation'); /** * @param {string} languagePath * @param {Documentation.Member} member * @param {string} text * @param {string=} paramOrOption * @returns {string} */ function createMarkdownLink(languagePath, member, text, paramOrOption) { if (!member.clazz) throw new Error('Member without a class!'); const className = toKebabCase(member.clazz.name); const memberName = toKebabCase(member.name); let hash = null; if (member.kind === 'property' || member.kind === 'method') hash = `${className}-${memberName}`.toLowerCase(); else if (member.kind === 'event') hash = `${className}-event-${memberName}`.toLowerCase(); if (paramOrOption) hash += '-option-' + toKebabCase(paramOrOption).toLowerCase(); return `[${text}](https://playwright.dev${languagePath}/docs/api/class-${member.clazz.name.toLowerCase()}#${hash})`; }; /** * @param {string} languagePath * @param {Documentation.Class} clazz * @returns {string} */ function createClassMarkdownLink(languagePath, clazz) { return `[${clazz.name}](https://playwright.dev${languagePath}/docs/api/class-${clazz.name.toLowerCase()})`; }; /** * @param {string} language * @param {OutputType} outputType * @returns {Documentation.Renderer} */ function docsLinkRendererForLanguage(language, outputType) { const languagePath = languageToRelativeDocsPath(language); return ({ clazz, member, param, option }) => { if (clazz) return createClassMarkdownLink(languagePath, clazz); if (!member || !member.clazz) throw new Error('Internal error'); if (param) return createMarkdownLink(languagePath, member, `\`${param.alias}\``, param.name); if (option) return createMarkdownLink(languagePath, member, `\`${option.alias}\``, option.name); const className = member.clazz.varName === 'playwrightAssertions' ? '' : member.clazz.varName + '.'; if (member.kind === 'method') { const args = outputType === 'ReleaseNotesMd' ? '' : renderJSSignature(member.argsArray); return createMarkdownLink(languagePath, member, `${formatClassName(className, language)}${member.alias}(${args})`); } if (member.kind === 'event') return createMarkdownLink(languagePath, member, `${className}on('${member.alias.toLowerCase()}')`); if (member.kind === 'property') return createMarkdownLink(languagePath, member, `${className}${member.alias}`); throw new Error('Unknown member kind ' + member.kind); } } function languageToRelativeDocsPath(language) { if (language === 'js') return ''; if (language === 'csharp') return '/dotnet'; if (language === 'python') return '/python'; if (language === 'java') return '/java'; throw new Error('Unexpected language ' + language); } function formatClassName(className, language) { if (!className.endsWith('Assertions.')) return className; className = className.substring(0, className.length - 1) if (language === 'js') return `expect(${assertionArgument(className)}).`; else if (language === 'csharp') return `Expect(${assertionArgument(className)}).`; else if (language === 'python') return `expect(${assertionArgument(className)}).`; else if (language === 'java') return `assertThat(${assertionArgument(className)}).`; throw new Error('Unexpected language ' + language); } function assertionArgument(className) { switch (className.toLowerCase()) { case 'locatorassertions': return 'locator'; case 'pageassertions': return 'page'; case 'genericassertions': return 'value'; case 'snapshotassertions': return 'value'; case 'apiresponseassertions': return 'response'; } throw new Error(`Unexpected assertion class: ${className}`); } /** * @param {Documentation.Member[]} args */ function renderJSSignature(args) { const tokens = []; let hasOptional = false; for (const arg of args) { const name = arg.alias; const optional = !arg.required; if (tokens.length) { if (optional && !hasOptional) tokens.push(`[, ${name}`); else tokens.push(`, ${name}`); } else { if (optional && !hasOptional) tokens.push(`[${name}`); else tokens.push(`${name}`); } hasOptional = hasOptional || optional; } if (hasOptional) tokens.push(']'); return tokens.join(''); } /** * @param {string} content * @param {string} languagePath * @param {string} relativePath * @returns {string} */ function renderPlaywrightDevLinks(content, languagePath, relativePath) { return content.replace(/\[([^\]]+)\]\((\.[^\)]+)\)/g, (match, p1, p2) => { return `[${p1}](${new URL(p2.replace('.md', ''), `https://playwright.dev${languagePath}/docs${relativePath}/`).toString()})`; }); } module.exports = { docsLinkRendererForLanguage, renderPlaywrightDevLinks, languageToRelativeDocsPath, }