| 
									
										
										
										
											2022-02-08 11:35:00 -07: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'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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-25 15:45:53 -06:00
										 |  |  |   async version() { | 
					
						
							|  |  |  |     const workspacePackageJSON = await readJSON(path.join(this._rootDir, 'package.json')); | 
					
						
							|  |  |  |     return workspacePackageJSON.version; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-08 11:35:00 -07: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); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // 2. Make sure package-lock and package's package.json are consistent.
 | 
					
						
							|  |  |  |       //    All manual package-lock management is a workaround for
 | 
					
						
							|  |  |  |       //    https://github.com/npm/cli/issues/3940
 | 
					
						
							|  |  |  |       const pkgLockEntry = packageLock['packages']['packages/' + path.basename(pkg.path)]; | 
					
						
							|  |  |  |       const depLockEntry = packageLock['dependencies'][pkg.name]; | 
					
						
							|  |  |  |       if (!pkg.isPrivate) { | 
					
						
							|  |  |  |         pkgLockEntry.version = version; | 
					
						
							|  |  |  |         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; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       for (const otherPackage of this._packages) { | 
					
						
							|  |  |  |         if (pkgLockEntry.dependencies && pkgLockEntry.dependencies[otherPackage.name]) | 
					
						
							|  |  |  |           pkgLockEntry.dependencies[otherPackage.name] = version; | 
					
						
							| 
									
										
										
										
											2022-04-07 15:46:43 -06:00
										 |  |  |         if (pkgLockEntry.devDependencies && pkgLockEntry.devDependencies[otherPackage.name]) | 
					
						
							|  |  |  |           pkgLockEntry.devDependencies[otherPackage.name] = version; | 
					
						
							| 
									
										
										
										
											2022-02-08 11:35:00 -07:00
										 |  |  |         if (depLockEntry.requires && depLockEntry.requires[otherPackage.name]) | 
					
						
							|  |  |  |           depLockEntry.requires[otherPackage.name] = version; | 
					
						
							|  |  |  |         if (pkg.packageJSON.dependencies && pkg.packageJSON.dependencies[otherPackage.name]) | 
					
						
							|  |  |  |           pkg.packageJSON.dependencies[otherPackage.name] = version; | 
					
						
							| 
									
										
										
										
											2022-04-07 15:46:43 -06:00
										 |  |  |         if (pkg.packageJSON.devDependencies && pkg.packageJSON.devDependencies[otherPackage.name]) | 
					
						
							|  |  |  |           pkg.packageJSON.devDependencies[otherPackage.name] = version; | 
					
						
							| 
									
										
										
										
											2022-02-08 11:35:00 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |       await maybeWriteJSON(pkg.packageJSONPath, pkg.packageJSON); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     await maybeWriteJSON(packageLockPath, packageLock); | 
					
						
							|  |  |  |     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'), | 
					
						
							|  |  |  |     // We copy README.md additionally for Playwright so that it looks nice on NPM.
 | 
					
						
							|  |  |  |     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'), | 
					
						
							|  |  |  |     files: LICENCE_FILES, | 
					
						
							|  |  |  |   }), | 
					
						
							|  |  |  |   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, | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2023-04-17 19:19:21 -04:00
										 |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/experimental-ct-core', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-core'), | 
					
						
							|  |  |  |     files: ['LICENSE'], | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2022-03-11 08:00:46 -08:00
										 |  |  |   new PWPackage({ | 
					
						
							| 
									
										
										
										
											2022-03-12 13:51:27 -08:00
										 |  |  |     name: '@playwright/experimental-ct-react', | 
					
						
							| 
									
										
										
										
											2022-03-11 08:00:46 -08:00
										 |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-react'), | 
					
						
							| 
									
										
										
										
											2022-03-12 13:51:27 -08:00
										 |  |  |     files: ['LICENSE'], | 
					
						
							| 
									
										
										
										
											2022-03-11 08:00:46 -08:00
										 |  |  |   }), | 
					
						
							| 
									
										
										
										
											2023-03-03 23:28:33 +01:00
										 |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/experimental-ct-react17', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-react17'), | 
					
						
							|  |  |  |     files: ['LICENSE'], | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2022-08-23 23:08:53 +02:00
										 |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/experimental-ct-solid', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-solid'), | 
					
						
							|  |  |  |     files: ['LICENSE'], | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2022-03-11 08:00:46 -08:00
										 |  |  |   new PWPackage({ | 
					
						
							| 
									
										
										
										
											2022-03-12 13:51:27 -08:00
										 |  |  |     name: '@playwright/experimental-ct-svelte', | 
					
						
							| 
									
										
										
										
											2022-03-11 08:00:46 -08:00
										 |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-svelte'), | 
					
						
							| 
									
										
										
										
											2022-03-12 13:51:27 -08:00
										 |  |  |     files: ['LICENSE'], | 
					
						
							| 
									
										
										
										
											2022-03-11 08:00:46 -08:00
										 |  |  |   }), | 
					
						
							|  |  |  |   new PWPackage({ | 
					
						
							| 
									
										
										
										
											2022-03-12 13:51:27 -08:00
										 |  |  |     name: '@playwright/experimental-ct-vue', | 
					
						
							| 
									
										
										
										
											2022-03-11 08:00:46 -08:00
										 |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-vue'), | 
					
						
							| 
									
										
										
										
											2022-03-12 13:51:27 -08:00
										 |  |  |     files: ['LICENSE'], | 
					
						
							| 
									
										
										
										
											2022-03-11 08:00:46 -08:00
										 |  |  |   }), | 
					
						
							| 
									
										
										
										
											2022-06-02 17:37:43 -07:00
										 |  |  |   new PWPackage({ | 
					
						
							|  |  |  |     name: '@playwright/experimental-ct-vue2', | 
					
						
							|  |  |  |     path: path.join(ROOT_PATH, 'packages', 'playwright-ct-vue2'), | 
					
						
							|  |  |  |     files: ['LICENSE'], | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2022-02-08 11:35:00 -07: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!`); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     '--list-public-package-paths': () => { | 
					
						
							|  |  |  |       for (const pkg of workspace.packages()) { | 
					
						
							|  |  |  |         if (!pkg.isPrivate) | 
					
						
							|  |  |  |           console.log(pkg.path); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2022-03-25 15:45:53 -06:00
										 |  |  |     '--get-version': async (version) => { | 
					
						
							|  |  |  |       console.log(await workspace.version()); | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2022-02-08 11:35:00 -07: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]); | 
					
						
							|  |  |  | } |