| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  | #!/usr/bin/env node
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Copyright 2017 Google Inc. All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-28 23:03:09 +08:00
										 |  |  | //@ts-check
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-11 22:52:17 +08:00
										 |  |  | const playwright = require('playwright-core'); | 
					
						
							| 
									
										
										
										
											2020-12-03 05:50:10 +08:00
										 |  |  | const fs = require('fs'); | 
					
						
							| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  | const path = require('path'); | 
					
						
							| 
									
										
										
										
											2021-01-08 07:00:04 +08:00
										 |  |  | const { parseApi } = require('./api_parser'); | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  | const missingDocs = require('./missingDocs'); | 
					
						
							| 
									
										
										
										
											2021-01-09 08:36:52 +08:00
										 |  |  | const md = require('../markdown'); | 
					
						
							| 
									
										
										
										
											2022-11-20 03:26:11 +08:00
										 |  |  | const docs = require('./documentation'); | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  | const toKebabCase = require('lodash/kebabCase') | 
					
						
							| 
									
										
										
										
											2020-02-14 10:26:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-08 07:00:04 +08:00
										 |  |  | /** @typedef {import('./documentation').Type} Type */ | 
					
						
							| 
									
										
										
										
											2020-12-29 09:38:00 +08:00
										 |  |  | /** @typedef {import('../markdown').MarkdownNode} MarkdownNode */ | 
					
						
							| 
									
										
										
										
											2020-12-28 23:03:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  | const PROJECT_DIR = path.join(__dirname, '..', '..'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  | const dirtyFiles = new Set(); | 
					
						
							| 
									
										
										
										
											2020-12-31 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-04 01:21:56 +08:00
										 |  |  | run().catch(e => { | 
					
						
							|  |  |  |   console.error(e); | 
					
						
							|  |  |  |   process.exit(1); | 
					
						
							|  |  |  | });; | 
					
						
							| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-17 04:51:39 +08:00
										 |  |  | function getAllMarkdownFiles(dirPath, filePaths = []) { | 
					
						
							|  |  |  |   for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) { | 
					
						
							|  |  |  |     if (entry.isFile() && entry.name.toLowerCase().endsWith('.md')) | 
					
						
							|  |  |  |       filePaths.push(path.join(dirPath, entry.name)); | 
					
						
							|  |  |  |     else if (entry.isDirectory()) | 
					
						
							|  |  |  |       getAllMarkdownFiles(path.join(dirPath, entry.name), filePaths); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return filePaths; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  | async function run() { | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |   // Patch README.md
 | 
					
						
							| 
									
										
										
										
											2021-05-18 15:29:48 +08:00
										 |  |  |   const versions = await getBrowserVersions(); | 
					
						
							| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |     const params = new Map(); | 
					
						
							|  |  |  |     const { chromium, firefox, webkit } = versions; | 
					
						
							|  |  |  |     params.set('chromium-version', chromium); | 
					
						
							|  |  |  |     params.set('firefox-version', firefox); | 
					
						
							|  |  |  |     params.set('webkit-version', webkit); | 
					
						
							|  |  |  |     params.set('chromium-version-badge', `[](https://www.chromium.org/Home)`); | 
					
						
							| 
									
										
										
										
											2022-12-20 06:47:37 +08:00
										 |  |  |     params.set('firefox-version-badge', `[](https://www.mozilla.org/en-US/firefox/new/)`); | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |     params.set('webkit-version-badge', `[](https://webkit.org/)`); | 
					
						
							| 
									
										
										
										
											2020-12-31 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |     let content = fs.readFileSync(path.join(PROJECT_DIR, 'README.md')).toString(); | 
					
						
							|  |  |  |     content = content.replace(/<!-- GEN:([^ ]+) -->([^<]*)<!-- GEN:stop -->/ig, (match, p1) => { | 
					
						
							|  |  |  |       if (!params.has(p1)) { | 
					
						
							|  |  |  |         console.log(`ERROR: Invalid generate parameter "${p1}" in "${match}"`); | 
					
						
							|  |  |  |         process.exit(1); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return `<!-- GEN:${p1} -->${params.get(p1)}<!-- GEN:stop -->`; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     writeAssumeNoop(path.join(PROJECT_DIR, 'README.md'), content, dirtyFiles); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 02:37:53 +08:00
										 |  |  |   let playwrightVersion = require(path.join(PROJECT_DIR, 'package.json')).version; | 
					
						
							|  |  |  |   if (playwrightVersion.endsWith('-next')) | 
					
						
							|  |  |  |     playwrightVersion = playwrightVersion.substring(0, playwrightVersion.indexOf('-next')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-24 00:22:54 +08:00
										 |  |  |   // Ensure browser versions in browsers.json. This is most important for WebKit
 | 
					
						
							|  |  |  |   // since its version is hardcoded in Playwright library rather then in browser builds.
 | 
					
						
							|  |  |  |   // @see https://github.com/microsoft/playwright/issues/15702
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     const browsersJSONPath = path.join(__dirname, '..', '..', 'packages/playwright-core/browsers.json'); | 
					
						
							|  |  |  |     const browsersJSON = JSON.parse(await fs.promises.readFile(browsersJSONPath, 'utf8')); | 
					
						
							|  |  |  |     for (const browser of browsersJSON.browsers) { | 
					
						
							|  |  |  |       if (versions[browser.name]) | 
					
						
							|  |  |  |         browser.browserVersion = versions[browser.name]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     writeAssumeNoop(browsersJSONPath, JSON.stringify(browsersJSON, null, 2) + '\n', dirtyFiles); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 15:29:48 +08:00
										 |  |  |   // Update device descriptors
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2021-10-11 22:52:17 +08:00
										 |  |  |     const devicesDescriptorsSourceFile = path.join(PROJECT_DIR, 'packages', 'playwright-core', 'src', 'server', 'deviceDescriptorsSource.json') | 
					
						
							| 
									
										
										
										
											2021-05-18 15:29:48 +08:00
										 |  |  |     const devicesDescriptors = require(devicesDescriptorsSourceFile) | 
					
						
							| 
									
										
										
										
											2021-07-23 02:01:18 +08:00
										 |  |  |     for (const deviceName of Object.keys(devicesDescriptors)) { | 
					
						
							|  |  |  |       switch (devicesDescriptors[deviceName].defaultBrowserType) { | 
					
						
							|  |  |  |         case 'chromium': | 
					
						
							|  |  |  |           devicesDescriptors[deviceName].userAgent = devicesDescriptors[deviceName].userAgent.replace( | 
					
						
							|  |  |  |             /(.*Chrome\/)(.*?)( .*)/, | 
					
						
							|  |  |  |             `$1${versions.chromium}$3` | 
					
						
							|  |  |  |           ).replace( | 
					
						
							|  |  |  |             /(.*Edg\/)(.*?)$/, | 
					
						
							|  |  |  |             `$1${versions.chromium}` | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         case 'firefox': | 
					
						
							|  |  |  |           devicesDescriptors[deviceName].userAgent = devicesDescriptors[deviceName].userAgent.replace( | 
					
						
							|  |  |  |             /^(.*Firefox\/)(.*?)( .*?)?$/, | 
					
						
							|  |  |  |             `$1${versions.firefox}$3` | 
					
						
							| 
									
										
										
										
											2021-11-24 01:56:22 +08:00
										 |  |  |           ).replace(/^(.*rv:)(.*)(\).*?)$/, `$1${versions.firefox}$3`) | 
					
						
							| 
									
										
										
										
											2021-07-23 02:01:18 +08:00
										 |  |  |           break; | 
					
						
							|  |  |  |         case 'webkit': | 
					
						
							|  |  |  |           devicesDescriptors[deviceName].userAgent = devicesDescriptors[deviceName].userAgent.replace( | 
					
						
							|  |  |  |             /(.*Version\/)(.*?)( .*)/, | 
					
						
							|  |  |  |             `$1${versions.webkit}$3` | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-07-15 23:41:23 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-24 05:03:41 +08:00
										 |  |  |     const invalidConfigurations = Object.entries(devicesDescriptors).filter(([_, deviceDescriptor]) => deviceDescriptor.isMobile && deviceDescriptor.defaultBrowserType === 'firefox').map(([deviceName, deviceDescriptor]) => deviceName); | 
					
						
							|  |  |  |     if (invalidConfigurations.length > 0) | 
					
						
							|  |  |  |       throw new Error(`Invalid Device Configurations. isMobile with Firefox not supported: ${invalidConfigurations.join(', ')}`); | 
					
						
							| 
									
										
										
										
											2021-05-18 15:29:48 +08:00
										 |  |  |     writeAssumeNoop(devicesDescriptorsSourceFile, JSON.stringify(devicesDescriptors, null, 2), dirtyFiles); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 06:32:46 +08:00
										 |  |  |   // Validate links/code snippet langs
 | 
					
						
							| 
									
										
										
										
											2021-01-09 08:36:52 +08:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  |     const langs = ['js', 'java', 'python', 'csharp']; | 
					
						
							| 
									
										
										
										
											2022-03-26 02:30:45 +08:00
										 |  |  |     const documentationRoot = path.join(PROJECT_DIR, 'docs', 'src'); | 
					
						
							| 
									
										
										
										
											2024-09-26 16:08:16 +08:00
										 |  |  |     const apiRoot = path.join(documentationRoot, 'api'); | 
					
						
							|  |  |  |     const testApiRoot = path.join(documentationRoot, 'test-api'); | 
					
						
							|  |  |  |     const testReporterApiRoot = path.join(documentationRoot, 'test-reporter-api'); | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  |     for (const lang of langs) { | 
					
						
							|  |  |  |       try { | 
					
						
							| 
									
										
										
										
											2024-09-26 16:08:16 +08:00
										 |  |  |         let documentation = parseApi(apiRoot); | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  |         if (lang === 'js') { | 
					
						
							| 
									
										
										
										
											2024-04-03 23:51:32 +08:00
										 |  |  |           documentation = documentation.mergeWith( | 
					
						
							| 
									
										
										
										
											2024-09-26 16:08:16 +08:00
										 |  |  |             parseApi(testApiRoot, path.join(documentationRoot, 'api', 'params.md')) | 
					
						
							| 
									
										
										
										
											2024-04-03 23:51:32 +08:00
										 |  |  |           ).mergeWith( | 
					
						
							| 
									
										
										
										
											2024-09-26 16:08:16 +08:00
										 |  |  |             parseApi(testReporterApiRoot) | 
					
						
							| 
									
										
										
										
											2024-04-03 23:51:32 +08:00
										 |  |  |           ); | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-04-03 23:51:32 +08:00
										 |  |  |         documentation.filterForLanguage(lang); | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // This validates member links.
 | 
					
						
							|  |  |  |         documentation.setLinkRenderer(() => undefined); | 
					
						
							| 
									
										
										
										
											2022-11-15 05:05:05 +08:00
										 |  |  |         // This validates code snippet groups in comments.
 | 
					
						
							|  |  |  |         documentation.setCodeGroupsTransformer(lang, tabs => tabs.map(tab => tab.spec)); | 
					
						
							|  |  |  |         documentation.generateSourceCodeComments(); | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  |         const mdLinks = []; | 
					
						
							|  |  |  |         const mdSections = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const cls of documentation.classesArray) { | 
					
						
							|  |  |  |           const filePath = path.join(documentationRoot, 'api', 'class-' + cls.name.toLowerCase() + '.md'); | 
					
						
							| 
									
										
										
										
											2024-04-03 23:51:32 +08:00
										 |  |  |           for (const member of cls.membersArray) { | 
					
						
							|  |  |  |             const memberHash = filePath + '#' + toKebabCase(cls.name).toLowerCase() + '-' + toKebabCase(member.name).toLowerCase() | 
					
						
							|  |  |  |             mdSections.add(memberHash); | 
					
						
							|  |  |  |             for (const arg of member.argsArray) { | 
					
						
							|  |  |  |               mdSections.add(memberHash + '-option-' + toKebabCase(arg.name).toLowerCase()); | 
					
						
							|  |  |  |               if (arg.name === "options" && arg.type) { | 
					
						
							|  |  |  |                 for (const option of arg.type.deepProperties()) | 
					
						
							|  |  |  |                   mdSections.add(memberHash + '-option-' + toKebabCase(option.name).toLowerCase()); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  |           for (const event of cls.eventsArray) | 
					
						
							|  |  |  |             mdSections.add(filePath + '#' + toKebabCase(cls.name).toLowerCase() + '-event-' + toKebabCase(event.name).toLowerCase()); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-03-26 02:30:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for (const filePath of getAllMarkdownFiles(documentationRoot)) { | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  |           if (!filePath.includes(`-${lang}`) && langs.some(other => other !== lang && filePath.includes(`-${other}`))) | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |           // Standardise naming and remove the filter in the file name
 | 
					
						
							|  |  |  |           // Also, Internally (playwright.dev generator) we merge test-api and test-reporter-api into api.
 | 
					
						
							|  |  |  |           const canonicalName = filePath.replace(/(-(js|python|csharp|java))+/, '').replace(/(\/|\\)(test-api|test-reporter-api)(\/|\\)/, `${path.sep}api${path.sep}`); | 
					
						
							|  |  |  |           mdSections.add(canonicalName); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-17 04:51:39 +08:00
										 |  |  |           const data = fs.readFileSync(filePath, 'utf-8'); | 
					
						
							| 
									
										
										
										
											2022-11-15 05:05:05 +08:00
										 |  |  |           let rootNode = md.filterNodesForLanguage(md.parse(data), lang); | 
					
						
							|  |  |  |           // Validates code snippet groups.
 | 
					
						
							| 
									
										
										
										
											2022-11-20 03:26:11 +08:00
										 |  |  |           rootNode = docs.processCodeGroups(rootNode, lang, tabs => tabs.map(tab => tab.spec)); | 
					
						
							| 
									
										
										
										
											2022-11-15 05:05:05 +08:00
										 |  |  |           // Renders links.
 | 
					
						
							| 
									
										
										
										
											2024-09-26 16:08:16 +08:00
										 |  |  |           if (!filePath.startsWith(apiRoot) && !filePath.startsWith(testApiRoot) && !filePath.startsWith(testReporterApiRoot)) | 
					
						
							|  |  |  |             documentation.renderLinksInNodes(rootNode); | 
					
						
							| 
									
										
										
										
											2022-11-15 05:05:05 +08:00
										 |  |  |           // Validate links.
 | 
					
						
							| 
									
										
										
										
											2022-03-26 02:30:45 +08:00
										 |  |  |           { | 
					
						
							|  |  |  |             md.visitAll(rootNode, node => { | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  |               if (node.type === 'code') { | 
					
						
							|  |  |  |                 const allowedCodeLangs = new Set([ | 
					
						
							|  |  |  |                   'csharp', | 
					
						
							|  |  |  |                   'java', | 
					
						
							| 
									
										
										
										
											2023-12-15 00:19:24 +08:00
										 |  |  |                   'css', | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  |                   'js', | 
					
						
							|  |  |  |                   'ts', | 
					
						
							|  |  |  |                   'python', | 
					
						
							|  |  |  |                   'py', | 
					
						
							|  |  |  |                   'java', | 
					
						
							|  |  |  |                   'powershell', | 
					
						
							|  |  |  |                   'batch', | 
					
						
							|  |  |  |                   'ini', | 
					
						
							|  |  |  |                   'txt', | 
					
						
							|  |  |  |                   'html', | 
					
						
							|  |  |  |                   'xml', | 
					
						
							|  |  |  |                   'yml', | 
					
						
							|  |  |  |                   'yaml', | 
					
						
							|  |  |  |                   'json', | 
					
						
							|  |  |  |                   'groovy', | 
					
						
							|  |  |  |                   'html', | 
					
						
							|  |  |  |                   'bash', | 
					
						
							|  |  |  |                   'sh', | 
					
						
							| 
									
										
										
										
											2023-12-01 06:04:42 +08:00
										 |  |  |                   'Dockerfile', | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  |                 ]); | 
					
						
							|  |  |  |                 if (!allowedCodeLangs.has(node.codeLang.split(' ')[0])) | 
					
						
							|  |  |  |                   throw new Error(`${path.relative(PROJECT_DIR, filePath)} contains code block with invalid code block language ${node.codeLang}`); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               if (node.type.startsWith('h')) { | 
					
						
							|  |  |  |                 const hash = mdSectionHash(node.text || ''); | 
					
						
							|  |  |  |                 mdSections.add(canonicalName + '#' + hash); | 
					
						
							| 
									
										
										
										
											2023-07-25 06:32:46 +08:00
										 |  |  |               } | 
					
						
							| 
									
										
										
										
											2022-03-26 02:30:45 +08:00
										 |  |  |               if (!node.text) | 
					
						
							|  |  |  |                 return; | 
					
						
							| 
									
										
										
										
											2024-04-03 23:51:32 +08:00
										 |  |  |               // Match links in a lax way (.+), so they can include spaces, backticks etc.
 | 
					
						
							|  |  |  |               for (const [, mdLinkName, mdLink] of node.text.matchAll(/\[(.+)\]\((.*?)\)/g)) { | 
					
						
							| 
									
										
										
										
											2022-03-26 02:30:45 +08:00
										 |  |  |                 const isExternal = mdLink.startsWith('http://') || mdLink.startsWith('https://'); | 
					
						
							|  |  |  |                 if (isExternal) | 
					
						
							|  |  |  |                   continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  |                 const [beforeHash, hash] = mdLink.split('#'); | 
					
						
							|  |  |  |                 let linkWithoutHash = canonicalName; | 
					
						
							|  |  |  |                 if (beforeHash) { | 
					
						
							|  |  |  |                   // Not same-file link.
 | 
					
						
							|  |  |  |                   linkWithoutHash = path.join(path.dirname(filePath), beforeHash); | 
					
						
							|  |  |  |                   if (path.extname(linkWithoutHash) !== '.md') | 
					
						
							|  |  |  |                     linkWithoutHash += '.md'; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 mdLinks.push({ filePath, linkTarget: linkWithoutHash + (hash ? '#' + hash : ''), name: mdLinkName }); | 
					
						
							| 
									
										
										
										
											2022-03-26 02:30:45 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const badLinks = []; | 
					
						
							|  |  |  |         for (const { filePath, linkTarget, name } of mdLinks) { | 
					
						
							|  |  |  |           if (!mdSections.has(linkTarget)) | 
					
						
							|  |  |  |             badLinks.push(`${path.relative(PROJECT_DIR, filePath)} references to '${linkTarget}' as '${name}' which does not exist.`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (badLinks.length) | 
					
						
							|  |  |  |           throw new Error('Broken links found:\n' + badLinks.join('\n')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  |       } catch (e) { | 
					
						
							|  |  |  |         e.message = `While processing "${lang}"\n` + e.message; | 
					
						
							|  |  |  |         throw e; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-01-09 08:36:52 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |   // Check for missing docs
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2021-08-27 01:59:33 +08:00
										 |  |  |     const apiDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api')); | 
					
						
							|  |  |  |     apiDocumentation.filterForLanguage('js'); | 
					
						
							| 
									
										
										
										
											2021-10-11 22:52:17 +08:00
										 |  |  |     const srcClient = path.join(PROJECT_DIR, 'packages', 'playwright-core', 'src', 'client'); | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |     const sources = fs.readdirSync(srcClient).map(n => path.join(srcClient, n)); | 
					
						
							| 
									
										
										
										
											2021-07-23 02:01:18 +08:00
										 |  |  |     const errors = missingDocs(apiDocumentation, sources, path.join(srcClient, 'api.ts')); | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |     if (errors.length) { | 
					
						
							|  |  |  |       console.log('============================'); | 
					
						
							|  |  |  |       console.log('ERROR: missing documentation:'); | 
					
						
							|  |  |  |       errors.forEach(e => console.log(e)); | 
					
						
							|  |  |  |       console.log('============================') | 
					
						
							|  |  |  |       process.exit(1); | 
					
						
							| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |   if (dirtyFiles.size) { | 
					
						
							|  |  |  |     console.log('============================') | 
					
						
							| 
									
										
										
										
											2021-10-09 04:43:23 +08:00
										 |  |  |     console.log('ERROR: generated files have changed, this is only error if happens in CI:'); | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |     [...dirtyFiles].forEach(f => console.log(f)); | 
					
						
							|  |  |  |     console.log('============================') | 
					
						
							|  |  |  |     process.exit(1); | 
					
						
							| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |   process.exit(0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @param {string} content | 
					
						
							|  |  |  |  * @param {Set<string>} dirtyFiles | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function writeAssumeNoop(name, content, dirtyFiles) { | 
					
						
							| 
									
										
										
										
											2021-01-04 00:47:29 +08:00
										 |  |  |   fs.mkdirSync(path.dirname(name), { recursive: true }); | 
					
						
							|  |  |  |   const oldContent = fs.existsSync(name) ? fs.readFileSync(name).toString() : ''; | 
					
						
							| 
									
										
										
										
											2021-01-02 07:17:27 +08:00
										 |  |  |   if (oldContent !== content) { | 
					
						
							|  |  |  |     fs.writeFileSync(name, content); | 
					
						
							|  |  |  |     dirtyFiles.add(name); | 
					
						
							| 
									
										
										
										
											2019-11-19 10:18:28 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-14 10:26:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-13 05:03:01 +08:00
										 |  |  | async function getBrowserVersions() { | 
					
						
							| 
									
										
										
										
											2020-12-22 10:09:55 +08:00
										 |  |  |   const names = ['chromium', 'firefox', 'webkit']; | 
					
						
							|  |  |  |   const browsers = await Promise.all(names.map(name => playwright[name].launch())); | 
					
						
							|  |  |  |   const result = {}; | 
					
						
							|  |  |  |   for (let i = 0; i < names.length; i++) { | 
					
						
							|  |  |  |     result[names[i]] = browsers[i].version(); | 
					
						
							| 
									
										
										
										
											2020-03-13 05:03:01 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-12-22 10:09:55 +08:00
										 |  |  |   await Promise.all(browsers.map(browser => browser.close())); | 
					
						
							|  |  |  |   return result; | 
					
						
							| 
									
										
										
										
											2020-03-13 05:03:01 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-03 23:51:32 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {string} text | 
					
						
							|  |  |  |  * @returns {string} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2023-07-27 07:15:07 +08:00
										 |  |  | function mdSectionHash(text) { | 
					
						
							|  |  |  |   return text.toLowerCase().replace(/\s/g, '-').replace(/[^-_a-z0-9]/g, '').replace(/^-+/, ''); | 
					
						
							|  |  |  | } |