| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  | #!/usr/bin/env node
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | // @ts-check
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Use the following command to typescheck this file: | 
					
						
							|  |  |  |  * npx tsc --target es2020  --watch --checkjs --noemit --moduleResolution node workspace.js | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const fs = require('fs'); | 
					
						
							|  |  |  | const path = require('path'); | 
					
						
							| 
									
										
										
										
											2024-01-19 03:33:23 +08:00
										 |  |  | const child_process = require('child_process'); | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | const readJSON = async (filePath) => JSON.parse(await fs.promises.readFile(filePath, 'utf8')); | 
					
						
							|  |  |  | const writeJSON = async (filePath, json) => { | 
					
						
							|  |  |  |   await fs.promises.writeFile(filePath, JSON.stringify(json, null, 2) + '\n'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PWPackage { | 
					
						
							|  |  |  |   constructor(descriptor) { | 
					
						
							|  |  |  |     this.name = descriptor.name; | 
					
						
							|  |  |  |     this.path = descriptor.path; | 
					
						
							|  |  |  |     this.files = descriptor.files; | 
					
						
							|  |  |  |     this.packageJSONPath = path.join(this.path, 'package.json'); | 
					
						
							|  |  |  |     this.packageJSON = JSON.parse(fs.readFileSync(this.packageJSONPath, 'utf8')); | 
					
						
							|  |  |  |     this.isPrivate = !!this.packageJSON.private; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Workspace { | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {string} rootDir | 
					
						
							|  |  |  |    * @param {PWPackage[]} packages | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   constructor(rootDir, packages) { | 
					
						
							|  |  |  |     this._rootDir = rootDir; | 
					
						
							|  |  |  |     this._packages = packages; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @returns {PWPackage[]} | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   packages() { | 
					
						
							|  |  |  |     return this._packages; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-26 05:45:53 +08:00
										 |  |  |   async version() { | 
					
						
							|  |  |  |     const workspacePackageJSON = await readJSON(path.join(this._rootDir, 'package.json')); | 
					
						
							|  |  |  |     return workspacePackageJSON.version; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * @param {string} version | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   async setVersion(version) { | 
					
						
							|  |  |  |     if (version.startsWith('v')) | 
					
						
							|  |  |  |       throw new Error('version must not start with "v"'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 1. update workspace's package.json (playwright-internal) with the new version
 | 
					
						
							|  |  |  |     const workspacePackageJSON = await readJSON(path.join(this._rootDir, 'package.json')); | 
					
						
							|  |  |  |     workspacePackageJSON.version = version; | 
					
						
							|  |  |  |     await writeJSON(path.join(this._rootDir, 'package.json'), workspacePackageJSON); | 
					
						
							|  |  |  |     // 2. make workspace consistent.
 | 
					
						
							|  |  |  |     await this.ensureConsistent(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async ensureConsistent() { | 
					
						
							|  |  |  |     let hasChanges = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const maybeWriteJSON = async (jsonPath, json) => { | 
					
						
							|  |  |  |       const oldJson = await readJSON(jsonPath); | 
					
						
							|  |  |  |       if (JSON.stringify(json) === JSON.stringify(oldJson)) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       hasChanges = true; | 
					
						
							|  |  |  |       console.warn('Updated', jsonPath); | 
					
						
							|  |  |  |       await writeJSON(jsonPath, json); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const workspacePackageJSON = await readJSON(path.join(this._rootDir, 'package.json')); | 
					
						
							|  |  |  |     const packageLockPath = path.join(this._rootDir, 'package-lock.json'); | 
					
						
							|  |  |  |     const packageLock = JSON.parse(await fs.promises.readFile(packageLockPath, 'utf8')); | 
					
						
							|  |  |  |     const version = workspacePackageJSON.version; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Make sure package-lock version is consistent with root package.json version.
 | 
					
						
							|  |  |  |     packageLock.version = version; | 
					
						
							|  |  |  |     packageLock.packages[""].version = version; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const pkg of this._packages) { | 
					
						
							|  |  |  |       // 1. Copy package files.
 | 
					
						
							|  |  |  |       for (const file of pkg.files) { | 
					
						
							|  |  |  |         const fromPath = path.join(this._rootDir, file); | 
					
						
							|  |  |  |         const toPath = path.join(pkg.path, file); | 
					
						
							|  |  |  |         await fs.promises.mkdir(path.dirname(pkg.path), { recursive: true }); | 
					
						
							|  |  |  |         await fs.promises.copyFile(fromPath, toPath); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 03:33:23 +08:00
										 |  |  |       // 2. Make sure package's package.jsons are consistent.
 | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  |       if (!pkg.isPrivate) { | 
					
						
							|  |  |  |         pkg.packageJSON.version = version; | 
					
						
							|  |  |  |         pkg.packageJSON.repository = workspacePackageJSON.repository; | 
					
						
							|  |  |  |         pkg.packageJSON.engines = workspacePackageJSON.engines; | 
					
						
							|  |  |  |         pkg.packageJSON.homepage = workspacePackageJSON.homepage; | 
					
						
							|  |  |  |         pkg.packageJSON.author = workspacePackageJSON.author; | 
					
						
							|  |  |  |         pkg.packageJSON.license = workspacePackageJSON.license; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-08-09 09:46:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  |       for (const otherPackage of this._packages) { | 
					
						
							|  |  |  |         if (pkg.packageJSON.dependencies && pkg.packageJSON.dependencies[otherPackage.name]) | 
					
						
							|  |  |  |           pkg.packageJSON.dependencies[otherPackage.name] = version; | 
					
						
							| 
									
										
										
										
											2022-04-08 05:46:43 +08:00
										 |  |  |         if (pkg.packageJSON.devDependencies && pkg.packageJSON.devDependencies[otherPackage.name]) | 
					
						
							|  |  |  |           pkg.packageJSON.devDependencies[otherPackage.name] = version; | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |       await maybeWriteJSON(pkg.packageJSONPath, pkg.packageJSON); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-02-25 21:23:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 03:33:23 +08:00
										 |  |  |     // Re-run npm i to make package-lock dirty.
 | 
					
						
							|  |  |  |     child_process.execSync('npm i'); | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  |     return hasChanges; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ROOT_PATH = path.join(__dirname, '..'); | 
					
						
							|  |  |  | const LICENCE_FILES = ['NOTICE', 'LICENSE']; | 
					
						
							|  |  |  | const workspace = new Workspace(ROOT_PATH, [ | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: 'playwright', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright'), | 
					
						
							| 
									
										
										
										
											2023-09-09 05:23:35 +08:00
										 |  |  |     // We copy README.md additionally for playwright so that it looks nice on NPM.
 | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  |     files: [...LICENCE_FILES, 'README.md'], | 
					
						
							|  |  |  |   }), | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: 'playwright-core', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-core'), | 
					
						
							|  |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/test', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-test'), | 
					
						
							| 
									
										
										
										
											2023-09-09 05:23:35 +08:00
										 |  |  |     // We copy README.md additionally for @playwright/test so that it looks nice on NPM.
 | 
					
						
							|  |  |  |     files: [...LICENCE_FILES, 'README.md'], | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  |   }), | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: 'playwright-webkit', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-webkit'), | 
					
						
							|  |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: 'playwright-firefox', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-firefox'), | 
					
						
							|  |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: 'playwright-chromium', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-chromium'), | 
					
						
							|  |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2025-02-25 21:23:38 +08:00
										 |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/client', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-client'), | 
					
						
							|  |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2025-01-28 06:49:38 +08:00
										 |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/experimental-tools', | 
					
						
							| 
									
										
										
										
											2025-03-11 03:36:52 +08:00
										 |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-mcp'), | 
					
						
							| 
									
										
										
										
											2025-01-28 06:49:38 +08:00
										 |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2023-08-27 22:24:35 +08:00
										 |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/browser-webkit', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-browser-webkit'), | 
					
						
							|  |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/browser-firefox', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-browser-firefox'), | 
					
						
							|  |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/browser-chromium', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-browser-chromium'), | 
					
						
							|  |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2023-04-18 07:19:21 +08:00
										 |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/experimental-ct-core', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-core'), | 
					
						
							|  |  |  |     files: ['LICENSE'], | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2022-03-12 00:00:46 +08:00
										 |  |  |   new PWPackage({ | 
					
						
							| 
									
										
										
										
											2022-03-13 05:51:27 +08:00
										 |  |  |     name: '@playwright/experimental-ct-react', | 
					
						
							| 
									
										
										
										
											2022-03-12 00:00:46 +08:00
										 |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-react'), | 
					
						
							| 
									
										
										
										
											2022-03-13 05:51:27 +08:00
										 |  |  |     files: ['LICENSE'], | 
					
						
							| 
									
										
										
										
											2022-03-12 00:00:46 +08:00
										 |  |  |   }), | 
					
						
							| 
									
										
										
										
											2023-03-04 06:28:33 +08:00
										 |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/experimental-ct-react17', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-react17'), | 
					
						
							|  |  |  |     files: ['LICENSE'], | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2022-03-12 00:00:46 +08:00
										 |  |  |   new PWPackage({ | 
					
						
							| 
									
										
										
										
											2022-03-13 05:51:27 +08:00
										 |  |  |     name: '@playwright/experimental-ct-svelte', | 
					
						
							| 
									
										
										
										
											2022-03-12 00:00:46 +08:00
										 |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-svelte'), | 
					
						
							| 
									
										
										
										
											2022-03-13 05:51:27 +08:00
										 |  |  |     files: ['LICENSE'], | 
					
						
							| 
									
										
										
										
											2022-03-12 00:00:46 +08:00
										 |  |  |   }), | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							| 
									
										
										
										
											2022-03-13 05:51:27 +08:00
										 |  |  |     name: '@playwright/experimental-ct-vue', | 
					
						
							| 
									
										
										
										
											2022-03-12 00:00:46 +08:00
										 |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-vue'), | 
					
						
							| 
									
										
										
										
											2022-03-13 05:51:27 +08:00
										 |  |  |     files: ['LICENSE'], | 
					
						
							| 
									
										
										
										
											2022-03-12 00:00:46 +08:00
										 |  |  |   }), | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  | ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if (require.main === module) { | 
					
						
							|  |  |  |   parseCLI(); | 
					
						
							|  |  |  | } else { | 
					
						
							|  |  |  |   module.exports = {workspace}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function die(message, exitCode = 1) { | 
					
						
							|  |  |  |   console.error(message); | 
					
						
							|  |  |  |   process.exit(exitCode); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function parseCLI() { | 
					
						
							|  |  |  |   const commands = { | 
					
						
							|  |  |  |     '--ensure-consistent': async () => { | 
					
						
							|  |  |  |       const hasChanges = await workspace.ensureConsistent(); | 
					
						
							|  |  |  |       if (hasChanges) | 
					
						
							|  |  |  |         die(`\n  ERROR: workspace is inconsistent! Run '//utils/workspace.js --ensure-consistent' and commit changes!`); | 
					
						
							| 
									
										
										
										
											2024-01-19 03:33:23 +08:00
										 |  |  |       // Ensure lockfileVersion is 3
 | 
					
						
							|  |  |  |       const packageLock = require(ROOT_PATH +  '/package-lock.json'); | 
					
						
							|  |  |  |       if (packageLock.lockfileVersion !== 3) | 
					
						
							|  |  |  |         die(`\n  ERROR: package-lock.json lockfileVersion must be 3`); | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  |     }, | 
					
						
							|  |  |  |     '--list-public-package-paths': () => { | 
					
						
							|  |  |  |       for (const pkg of workspace.packages()) { | 
					
						
							|  |  |  |         if (!pkg.isPrivate) | 
					
						
							|  |  |  |           console.log(pkg.path); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2022-03-26 05:45:53 +08:00
										 |  |  |     '--get-version': async (version) => { | 
					
						
							|  |  |  |       console.log(await workspace.version()); | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2022-02-09 02:35:00 +08:00
										 |  |  |     '--set-version': async (version) => { | 
					
						
							|  |  |  |       if (!version) | 
					
						
							|  |  |  |         die('ERROR: Please specify version! e.g. --set-version 1.99.2'); | 
					
						
							|  |  |  |       await workspace.setVersion(version); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     '--help': () => { | 
					
						
							|  |  |  |       console.log([ | 
					
						
							|  |  |  |         `Available commands:`, | 
					
						
							|  |  |  |         ...Object.keys(commands).map(cmd => '  ' + cmd), | 
					
						
							|  |  |  |       ].join('\n')); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   const handler = commands[process.argv[2]]; | 
					
						
							|  |  |  |   if (!handler) | 
					
						
							|  |  |  |     die('ERROR: wrong usage! Run with --help to list commands'); | 
					
						
							|  |  |  |   await handler(process.argv[3]); | 
					
						
							|  |  |  | } |