mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	 cb1b64289c
			
		
	
	
		cb1b64289c
		
			
		
	
	
	
	
		
			
			This patch starts downloading FFMPEG like we download our browsers instead of bundling it in the NPM package. With this patch, NPM size is reduced from 8.8MB to 1.7MB. Consequences: - `npx playwright` is drastically faster now - playwright driver for language bindings is way smaller - projects that bundle Playwright can pass Apple Notorization Fixes #5193
		
			
				
	
	
		
			217 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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.
 | |
|  */
 | |
| const fs = require('fs');
 | |
| const os = require('os');
 | |
| const path = require('path');
 | |
| const rmSync = require('rimraf').sync;
 | |
| const ncp = require('ncp');
 | |
| const {spawnSync} = require('child_process');
 | |
| const util = require('util');
 | |
| 
 | |
| const writeFileAsync = util.promisify(fs.writeFile.bind(fs));
 | |
| const cpAsync = util.promisify(ncp);
 | |
| 
 | |
| const SCRIPT_NAME = path.basename(__filename);
 | |
| const ROOT_PATH = path.join(__dirname, '..');
 | |
| 
 | |
| const PLAYWRIGHT_CORE_FILES = ['bin/PrintDeps.exe', 'lib', 'types', 'NOTICE', 'LICENSE'];
 | |
| 
 | |
| const PACKAGES = {
 | |
|   'playwright': {
 | |
|     description: 'A high-level API to automate web browsers',
 | |
|     browsers: ['chromium', 'firefox', 'webkit', 'ffmpeg'],
 | |
|     // We copy README.md additionally for Playwright so that it looks nice on NPM.
 | |
|     files: [...PLAYWRIGHT_CORE_FILES, 'README.md'],
 | |
|   },
 | |
|   'playwright-core': {
 | |
|     description: 'A high-level API to automate web browsers',
 | |
|     browsers: [],
 | |
|     files: PLAYWRIGHT_CORE_FILES,
 | |
|   },
 | |
|   'playwright-webkit': {
 | |
|     description: 'A high-level API to automate WebKit',
 | |
|     browsers: ['webkit'],
 | |
|     files: PLAYWRIGHT_CORE_FILES,
 | |
|   },
 | |
|   'playwright-firefox': {
 | |
|     description: 'A high-level API to automate Firefox',
 | |
|     browsers: ['firefox'],
 | |
|     files: PLAYWRIGHT_CORE_FILES,
 | |
|   },
 | |
|   'playwright-chromium': {
 | |
|     description: 'A high-level API to automate Chromium',
 | |
|     browsers: ['chromium', 'ffmpeg'],
 | |
|     files: [...PLAYWRIGHT_CORE_FILES],
 | |
|   },
 | |
|   'playwright-electron': {
 | |
|     version: '0.4.0', // Manually manage playwright-electron version.
 | |
|     description: 'A high-level API to automate Electron',
 | |
|     browsers: ['ffmpeg'],
 | |
|     files: [...PLAYWRIGHT_CORE_FILES],
 | |
|   },
 | |
|   'playwright-android': {
 | |
|     version: '0.0.8', // Manually manage playwright-android version.
 | |
|     description: 'A high-level API to automate Chrome for Android',
 | |
|     browsers: ['ffmpeg'],
 | |
|     files: [...PLAYWRIGHT_CORE_FILES, 'bin/android-driver.apk', 'bin/android-driver-target.apk'],
 | |
|   },
 | |
| };
 | |
| 
 | |
| // 1. Parse CLI arguments
 | |
| const args = process.argv.slice(2);
 | |
| if (args.some(arg => arg === '--help')) {
 | |
|   console.log(usage());
 | |
|   process.exit(1);
 | |
| } else if (args.length < 1) {
 | |
|   console.log(`Please specify package name, e.g. 'playwright' or 'playwright-chromium'.`);
 | |
|   console.log(`Try running ${SCRIPT_NAME} --help`);
 | |
|   process.exit(1);
 | |
| } else if (args.length < 2) {
 | |
|   console.log(`Please specify output path`);
 | |
|   console.log(`Try running ${SCRIPT_NAME} --help`);
 | |
|   process.exit(1);
 | |
| }
 | |
| 
 | |
| const packageName = args[0];
 | |
| const outputPath = path.resolve(args[1]);
 | |
| const packagePath = path.join(__dirname, 'output', packageName);
 | |
| const package = PACKAGES[packageName];
 | |
| if (!package) {
 | |
|   console.log(`ERROR: unknown package ${packageName}`);
 | |
|   process.exit(1);
 | |
| }
 | |
| 
 | |
| // 2. Setup cleanup if needed
 | |
| if (!args.some(arg => arg === '--no-cleanup')) {
 | |
|   process.on('exit', () => {
 | |
|     rmSync(packagePath, {});
 | |
|   });
 | |
|   process.on('SIGINT', () => process.exit(2));
 | |
|   process.on('SIGHUP', () => process.exit(3));
 | |
|   process.on('SIGTERM', () => process.exit(4));
 | |
|   process.on('uncaughtException', error => {
 | |
|     console.error(error);
 | |
|     process.exit(5);
 | |
|   });
 | |
|   process.on('unhandledRejection', error => {
 | |
|     console.error(error);
 | |
|     process.exit(6);
 | |
|   });
 | |
| }
 | |
| 
 | |
| (async () => {
 | |
|   // 3. Copy package files.
 | |
|   rmSync(packagePath, {});
 | |
|   fs.mkdirSync(packagePath, { recursive: true });
 | |
|   await copyToPackage(path.join(__dirname, 'common') + path.sep, packagePath + path.sep);
 | |
|   if (fs.existsSync(path.join(__dirname, packageName))) {
 | |
|     // Copy package-specific files, these can overwrite common ones.
 | |
|     await copyToPackage(path.join(__dirname, packageName) + path.sep, packagePath + path.sep);
 | |
|   }
 | |
|   for (const file of package.files)
 | |
|     await copyToPackage(path.join(ROOT_PATH, file), path.join(packagePath, file));
 | |
| 
 | |
|   await copyToPackage(path.join(ROOT_PATH, 'api.json'), path.join(packagePath, 'api.json'));
 | |
|   await copyToPackage(path.join(ROOT_PATH, 'src/protocol/protocol.yml'), path.join(packagePath, 'protocol.yml'));
 | |
| 
 | |
|   // 4. Generate package.json
 | |
|   const pwInternalJSON = require(path.join(ROOT_PATH, 'package.json'));
 | |
|   await writeToPackage('package.json', JSON.stringify({
 | |
|     name: packageName,
 | |
|     version: package.version || pwInternalJSON.version,
 | |
|     description: package.description,
 | |
|     repository: pwInternalJSON.repository,
 | |
|     engines: pwInternalJSON.engines,
 | |
|     homepage: pwInternalJSON.homepage,
 | |
|     main: 'index.js',
 | |
|     bin: {
 | |
|       playwright: './lib/cli/cli.js',
 | |
|     },
 | |
|     exports: {
 | |
|       // Root import: we have a wrapper ES Module to support the following syntax.
 | |
|       // const { chromium } = require('playwright');
 | |
|       // import { chromium } from 'playwright';
 | |
|       '.': {
 | |
|         import: './index.mjs',
 | |
|         require: './index.js',
 | |
|       },
 | |
|       // Anything else can be required/imported by providing a relative path.
 | |
|       './': './',
 | |
|     },
 | |
|     scripts: {
 | |
|       install: 'node install.js',
 | |
|     },
 | |
|     author: pwInternalJSON.author,
 | |
|     license: pwInternalJSON.license,
 | |
|     dependencies: pwInternalJSON.dependencies
 | |
|   }, null, 2));
 | |
| 
 | |
|   // 5. Generate browsers.json
 | |
|   const browsersJSON = require(path.join(ROOT_PATH, 'browsers.json'));
 | |
|   for (const browser of browsersJSON.browsers)
 | |
|     browser.download = package.browsers.includes(browser.name);
 | |
|   await writeToPackage('browsers.json', JSON.stringify(browsersJSON, null, 2));
 | |
| 
 | |
|   // 6. Bake commit SHA into the package
 | |
|   const commitSHA = spawnSync('git', ['rev-parse', 'HEAD'], {cwd: __dirname, encoding: 'utf8'});
 | |
|   await writeToPackage('commitinfo', commitSHA.stdout.trim());
 | |
| 
 | |
|   // 7. Run npm pack
 | |
|   const shell = os.platform() === 'win32';
 | |
|   const {stdout, stderr, status} = spawnSync('npm', ['pack'], {cwd: packagePath, encoding: 'utf8', shell});
 | |
|   if (status !== 0) {
 | |
|     console.log(`ERROR: "npm pack" failed`);
 | |
|     console.log(stderr);
 | |
|     process.exit(1);
 | |
|   }
 | |
|   const tgzName = stdout.trim();
 | |
| 
 | |
|   // 7. Move result to the outputPath
 | |
|   fs.renameSync(path.join(packagePath, tgzName), outputPath);
 | |
|   console.log(outputPath);
 | |
| })();
 | |
| 
 | |
| async function writeToPackage(fileName, content) {
 | |
|   const toPath = path.join(packagePath, fileName);
 | |
|   console.error(`- generating: //${path.relative(ROOT_PATH, toPath)}`);
 | |
|   await writeFileAsync(toPath, content);
 | |
| }
 | |
| 
 | |
| async function copyToPackage(fromPath, toPath) {
 | |
|   console.error(`- copying: //${path.relative(ROOT_PATH, fromPath)} -> //${path.relative(ROOT_PATH, toPath)}`);
 | |
|   try {
 | |
|     fs.mkdirSync(path.dirname(toPath), { recursive: true });
 | |
|   } catch (e) {
 | |
|     // the folder might exist already
 | |
|   }
 | |
|   await cpAsync(fromPath, toPath);
 | |
| }
 | |
| 
 | |
| function usage() {
 | |
|   return `
 | |
| usage: ${SCRIPT_NAME} <package-name> <output-path> [--no-cleanup]
 | |
| 
 | |
| Creates a .tgz of the package and saves it at the given output path
 | |
| 
 | |
|   --no-cleanup    skip cleaning up generated files from package directory
 | |
| 
 | |
| Example:
 | |
|   ${SCRIPT_NAME} playwright ./playwright.tgz
 | |
| `;
 | |
| }
 | |
| 
 |